/**
 * @namespace Rsd
 * @copyright redmicro all Copyright (c)
 * @author Created by seeker910 on 14-3-18.
 */

/**
 *
 * @constructor Redjs
 * @param config
 * */
function Redjs(config) {

    //console.debug(Date.now(),'redjs begin');

    Rsd.apply(this, config || {});

    /**
     * 
     */
    Rsd.showLicense = function showLicense() {

        var file = Rsd.getRedjsHost() + Rsd.license;

        Rsd.loadFile(file, function (str) {
            var txt = str.replaceAll('\n', '<br>');
            //console.log(txt);
            txt = decodeURIComponent(txt);
            Rsd.showHtml('License查看', txt, '70%', '90%', 0);
        });

    };
    /**
     * redjs 框架正在加载
     */
    this.isLoading = function isLoading() {
        //没有开始时间时间,表示未加载
        if (this.__loadStartTime == undefined || this.__loadStartTime == undefined || !this.__loadStartTime) {
            return false;
        }
        //没有加载结束时间，表示加载中
        return this.__loadEndTime == undefined || this.__loadEndTime == null || !this.__loadEndTime
    };
    /**
     * @public
     * @description 验证浏览是否兼容redjs框架
     * @function
     * @memberof Redjs
     */
    this.validateBrowser = function validateBrowser() {
        return typeof Element !== "undefined";
    };

    /**
     * @public
     * @description 兼容处理，定义EventListener对象
     * @function
     * @memberof Redjs
     * */
    this.defineEventListener = function defineEventListener() {
        //"use strict";
        if (this.validateBrowser() == false) {
            return
        }


        (function () {
            var _add = 'addEventListener';

            if (Element.prototype.hasOwnProperty(_add)) {
                return;
            }

            Element.prototype[_add] == function (name, fn, useCapture) {

                if (this.addEventListener) {
                    this.addEventListener(name, fn, useCapture);

                } else if (this.attachEvent) {
                    this.attachEvent('on' + name, fn)
                }
                else {
                    this['on' + name] = fn;
                }
            };
        })();
        (function () {
            var _remove = 'removeEventListener';
            if (Element.prototype.hasOwnProperty(_remove)) {
                return;
            }
            Element.prototype[_remove] == function (name, fn, useCapture) {
                if (this.removeEventListener) {
                    this.removeEventListener(name, fn, useCapture);

                } else if (this.detachEvent) {
                    this.detachEvent('on' + name, fn)
                }
                else {
                    delete this['on' + name];
                }
            };
        })();

    };

    /**
     * @public
     * @description 兼容处理，定义ClassList对象
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.defineClassList = function defineClassList(type) {
        //"use strict";
        if (this.validateBrowser() == false) {
            return
        }
        var classListPropertyName = "classList";
        if (type.prototype.hasOwnProperty(classListPropertyName)) {
            return;
        }
        var trim = /^\s+|\s+$/g;
        var setClasses = function (elem, classes) {
            elem.className = classes.join(" ");
        };

        var checkAndGetIndex = function (classes, token) {
            if (token === "") {
                throw "SYNTAX_ERR";
            }
            if (/\s/.test(token)) {
                throw "INVALID_CHARACTER_ERR";
            }

            return classes.indexOf(token);
        };

        var classListGetter = function () {

            var elem = this;
            var classes;
            if (typeof (elem.className) == '[object String]') {
                classes = elem.className.replace(trim, "").split(/\s+/);
            } else {
                classes = [];
            }


            return {
                length: classes.length,
                item: function (i) {
                    return classes[i] || null;
                },
                contains: function (token) {
                    return checkAndGetIndex(classes, token) !== -1;
                },
                add: function (token) {
                    if (checkAndGetIndex(classes, token) === -1) {
                        classes.push(token);
                        this.length = classes.length;
                        setClasses(elem, classes);
                    }
                },
                remove: function (token) {
                    var index = checkAndGetIndex(classes, token);
                    if (index !== -1) {
                        classes.splice(index, 1);
                        this.length = classes.length;
                        setClasses(elem, classes);
                    }
                },
                toggle: function (token) {
                    if (checkAndGetIndex(classes, token) === -1) {
                        this.add(token);
                    } else {
                        this.remove(token);
                    }
                },
                toString: function () {
                    return elem.className;
                }
            };
        };

        if (Object.defineProperty) {
            Object.defineProperty(type.prototype, classListPropertyName, { get: classListGetter, enumerable: true });
        } else if (Object.prototype.__defineGetter__) {
            type.prototype.__defineGetter__(classListPropertyName, classListGetter);
        }

    };

    /**
     *
     *窗体自适应
     * */
    this.defineRem = function defineRem(maxWidth) {
        var doc = document;

        var _maxWidth = maxWidth || 640;
        var docEl = doc.documentElement;
        var clientWidth = docEl.clientWidth;
        if (!clientWidth) return;

        if (this.isMobile()) {
            this.___rem = 14;
            if (clientWidth < _maxWidth) {
                this.___rem = 14 * (clientWidth / _maxWidth);
            }
        } else {
            this.___rem = 12;
        }

        docEl.style.fontSize = this.___rem + 'px';
    };

    /*
    * */
    this.getRem = function getRem() {

        if (this.isEmpty(this.___rem)) {
            var docEl = doc.documentElement;
            this.___rem = parseFloat(docEl.style.fontSize);
        }
        return this.___rem;
    };

    /**
     * @public
     * @description 兼容处理，定义全屏功能;在window对象上定义以下方法： window.isFullScreen(),window.requestFullScreen(),window.cancelFullScreen()
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.defineFullScreen = function defineFullScreen() {
        /*
         Native FullScreen JavaScript API
         Assumes Mozilla naming conventions instead of W3C for now
         */
        var fullScreenApi = {

            supportsFullScreen: false,
            isFullScreen: function () {
                return false;
            },
            requestFullScreen: function () {
            },
            cancelFullScreen: function () {
            },
            fullScreenEventName: '',
            prefix: ''
        };
        var browserPrefixes = 'webkit moz o ms khtml'.split(' ');

        // check for native support
        if (typeof document.cancelFullScreen != 'undefined') {
            fullScreenApi.supportsFullScreen = true;
        }
        else {
            // check for fullscreen support by vendor prefix
            for (var i = 0, il = browserPrefixes.length; i < il; i++) {
                fullScreenApi.prefix = browserPrefixes[i];

                if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
                    fullScreenApi.supportsFullScreen = true;
                    break;
                }
            }
        }

        // update methods to do something useful
        if (fullScreenApi.supportsFullScreen) {

            fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';

            fullScreenApi.isFullScreen = function () {
                switch (this.prefix) {
                    case '':
                        return document.fullScreen;
                    case 'webkit':
                        return document.webkitIsFullScreen;
                    default:
                        return document[this.prefix + 'FullScreen'];
                }
            }
            fullScreenApi.requestFullScreen = function (el) {
                return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
            }
            fullScreenApi.cancelFullScreen = function (el) {
                return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
            }
        }

        // 将方法注册到 jQuery中
        if (typeof jQuery != 'undefined') {

            jQuery.fn.requestFullScreen = function () {

                return this.each(function () {
                    var el = jQuery(this);
                    if (fullScreenApi.supportsFullScreen) {
                        fullScreenApi.requestFullScreen(el);
                    }
                });
            };
        }

        // export api
        this.apply(window, fullScreenApi);


    };

    /**
     * 
     * @returns 
     */
    this.fullScreen = function fullScreen(element) {

        if (window.supportsFullScreen == false) {
            alert('SORRY: Your browser does not support FullScreen');
            return;
        }

        if (window.isFullScreen()) {
            window.cancelFullScreen(element || document.body);
        }
        else {
            window.requestFullScreen(element || document.body);
        }

        return window.isFullScreen();
    }

    /**
     * @public
     * @description重写 window.console 对象
     * @function
     * @memberof Redjs
     * */
    this.defineConsole = function defineConsole() {
        if (!window.console) {
            var console = {};
            console.info = function (message) {
                //alert(message);
            };
            console.log = function (message) {
                alert(message);
            };
            console.warn = function (message) {
                alert(message);
            };
            console.error = function (message) {
                alert(message);
            };
            window.console = console;
        }
    };

    /**
     * @public
     * @description 创建一个前端程序
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.createApplication = function createApplication(config) {

        var me = Rsd || this;
        if (me.isEmpty(config)) {
            console.error("配置不得为空")
            return;
        }
        if (me.app) {
            window.alert('当前页面redjs application已创建，不可重复创建。');
        }
        me.isDebug = config.isDebug ? true : false;
        me.isDemo = config.isDemo ? true : false;

        config.launch = config.launch || function () {
            alert('config launch funtion is null.')
        };

        var _config = me.apply(me.app, config);

        _config.appName = config.appName || 'unamed_application';
        _config.appFolder = config.appFolder || './src';
        _config.getUserUrl = config.getUserUrl || 'authority/user';
        _config.indexUrl = config.indexUrl || 'login.html';
        _config.appEdition = config.appEdition || 'beta';
        _config.appVersion = config.appVersion || '3.0';
        _config.logoUrl = config.version || 'resources/images/sys/logo.jpg';
        _config.requires = config.requires || [];

        me.app = me.create('Rsd.common.Application', _config);
        me.app.createdTime = Date.now();

        //console.debug(me.app.createdTime,'app created.');

        if (me.isMobile()) {

            window.onerror = function (error) {
                alert(error);
            }
        }

        /*
         * */
        me.onReady(function appStartup() {

            //console.debug(Date.now(),'app startup...');
            me.app.startTime = new Date().getTime();

            //等待框架加载
            var _id = setInterval(function () {
                //console.trace(_id);
                if (Rsd.isLoading()) {
                    return;
                }

                clearInterval(_id);
                //程序启动前执行
                if (me.isFunction(me.app.beforeRun)) {
                    me.app.beforeRun.call(me.app);
                }
                //console.trace('me.app.run');
                me.app.run();

            }, 100)


        });

        return me.app;
    };

    /*
    *
    * */
    this.clearHotKey = function clearHotKey(key) {
        if (this.isEmpty(this.__hotKey)) {
            return;
        }
        if (arguments.length == 0) {
            this.__hotKey = {};
            return;
        }
        delete this.__hotKey[key.toLowerCase()];
    };
    /**
     *@desc 注册全局热键
     * */
    this.registerHotKey = function hotKey(key, fn, caller, args) {
        this.__hotKey = this.__hotKey || {};
        this.__hotKey[key.toLowerCase()] = { fn: fn, caller: caller, args: args };
    };
    /*
    *
    * */
    this.exeHotKeyFun = function exeHotKeyFun(key) {
        if (this.isEmpty(key)) {
            return;
        }
        if (this.isEmpty(this.__hotKey)) {
            return;
        }
        var _hotKey = this.__hotKey[key.toLowerCase()];
        if (_hotKey) {
            this.callFunction(_hotKey.caller || this, _hotKey.fn, _hotKey.args);
        }

    };

    /**
     * @public
     * @description 注册事件，在 document onload 事件执行结束
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.onReady = function onReady(callback) {

        var me = Rsd || this;

        var _fn = function () {

            if (me.isFunction(callback)) {

                callback.call();
            }

            window.Rsd.events.fire(null, 'ready', arguments);
        }

        if (me.isReady()) {
            _fn();
        }
        else {
            if (document.addEventListener) {
                document.addEventListener("DOMContentLoaded", _fn, false);
            }
        }

    };
    /**  
    *  DOMContentLoaded 事件 即：HTML 文档被加载和解析完成后
    * */
    this.isReady = function isReady() {
        return this.__readyTime > 0;
    }
    /**
     * @public
     * @description 注册 document Resize事件
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.onResize = function onResize(obj, callback) {

        var me = Rsd || this;
        var events = me.events;
        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
        events.add(obj, resizeEvt, callback);
    };

    /** 
     * @description 注销 document Resize事件
    */
    this.unResize = function unResize(callback) {

        var me = Rsd || this;
        var _fn = callback;
        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
        var events = me.events;
        events.remove(resizeEvt, _fn);
    };
    /**
     *  @description 触发 document Resize事件
     * @param {*} evt 
     * @returns 
     */
    this.fireResize = function fireResize(evt) {

        var me = Rsd || this;

        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
        var events = me.events;
        events.fire(me, resizeEvt, evt);
    };

    /**
     * @description  被拖动源对象事件,即用户开始拖动元素时触发;通常用于设置拖动时的数据类型和数据。可以通过event.dataTransfer.setData()方法设置数据类型和数据。
     * @param dom 被拖动对象
     * @param callback 回调方法
     */
     "use strict";
    this.onDragStart = function onDragStart(dom, callback) {

        if(!dom)
        {
            throw new Error('argument dom is not allow empty');
        }
        var _dom = dom;
        _dom.addEventListener('dragstart', function (evt) {
            //evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件

            if (Rsd.isFunction(callback)) {
                callback(evt);
            }
            else
            {
                console.error('callback is not function object',callback)
            }
        });
    };

    /**
     * @public
     * @description 被拖动源对象事件，被拖动元素正在拖动时触发
     * @description Fires on the source object continuously during a drag operation. 
     * @function
     * @param dom 被拖动对象
     * @param callback 回调方法
     * @memberof Redjs
     * */
    "use strict";
    this.onDrag = function onDrag(dom, callback) {

        if(!dom)
        {
            throw new Error('argument dom is not allow empty');
        }
        var _dom = dom;
        _dom.addEventListener('drag',function (evt) {
             
            //evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件

            if (Rsd.isFunction(callback)) {
                callback(evt);
            }
            else
            {
                console.error('callback is not function object',callback)
            }
        });
    };
   
    /** 
    * @description 被拖动源对象事件，用户完成元素拖动后触发
    * @description Fires on the source object when the user releases the mouse at the close of a drag operation.
    * 
    * @param dom 被拖动对象
    * @param callback 回调方法
    * */
    "use strict";
    this.onDragEnd = function onDragEnd(dom, callback) {

        if(!dom)
        {
            throw new Error('argument dom is not allow empty');
        }
        var _dom = dom;
        _dom.addEventListener('dragend', function (evt) {
            
            //evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件

            if (Rsd.isFunction(callback)) {
                callback(evt);
            }
            else
            {
                console.error('callback is not function object',callback)
            }
        });
    };
    //缓存
    this.___gbl_fns=this.___gbl_fns||{dragover:{},dragenter:{},dragleave:{},drop:{}};
    /** 
    * @description onDragOver拖动过程对象事件，在拖动元素在目标元素上方悬停时触发，通常用于防止默认的拖放行为。可以通过event.preventDefault()方法阻止默认行为。
    * @description Fires on the target element continuously while the user drags the object over a valid drop target.
    * @function
    * @param dom 被拖动对象的容器，null为document
    * @param name 事件标识
    * @param callback  回调方法
    * */
    "use strict";
    this.onDragOver = function onDragOver(dom,name, callback) { 
         
        var _fn = function (evt) {
            evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件
            if (Rsd.isFunction(callback)) {
                callback(evt);
            }else
            {
                console.error('callback is not function object',callback)
            }
        };
        this.___gbl_fns['dragover'][name] = _fn;

        var _dom = dom || document;
        if(_dom == document)
        {
            console.debug('为document注册onDragOver事件时，需要主动注销事件('+name+')'); 
        }
        _dom.addEventListener('dragover',_fn);
    };
    /**
     * 
     */
    this.offDragOver = function offDragOver(dom, name)
    { 
         var _fn = this.___gbl_fns['dragover'][name];
         if(_fn)
         { 
            var _dom = dom || document;
            _dom.removeEventListener('dragover',_fn);
            delete this.___gbl_fns['dragover'][name];
            console.debug('注销dragover事件:'+name); 
         } 
    },
    /** 
    * @description onDragEnter拖动过程对象事件，在拖动元素进入目标元素时触发，通常用于设置拖动时的样式。
    * @description Fires on the target element when the user drags the object to a valid drop target. 
    * @function
     * @param dom 被拖动对象进入的容器，null为document
     * @param name 事件标识
     * @param callback  回调方法
    * */
    this.onDragEnter = function onDragEnter(dom,name, callback) {  
        var _fn = function (evt) {
            evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件
            if (Rsd.isFunction(callback)) {
                callback(evt);
            }else
            {
                console.error('callback is not function object',callback)
            }
        }
        this.___gbl_fns['dragenter'][name] = _fn;
        var _dom = dom || document;
        if(_dom == document)
        {
            console.debug('为document注册onDragEnter事件时，需要主动注销事件('+name+')');
        }
        _dom.addEventListener('dragenter', _fn);
    };
    /***
     * 
     */
    this.offDragEnter = function offDragEnter(dom, name)
    { 
         var _dom = dom || document;
         var _fn = this.___gbl_fns['dragenter'][name];
         if(_fn)
         {
            _dom.removeEventListener('dragenter',_fn);
            delete this.___gbl_fns['dragenter'][name];
            console.debug('注销dragenter事件:'+name); 
         } 
    },
    /**
    * @description onDragLeave拖动过程对象事件，在拖动元素离开目标元素时触发，通常用于设置拖动时的样式。
    * @description Fires on the target object when the user moves the mouse out of a valid drop target during a drag operation.
    * @function
     * @param dom 被拖动对象的容器，null为document
     * @param name 事件标识
     * @param callback  回调方法
    * */
    "use strict";
    this.onDragLeave = function onDragLeave(dom,name, callback) {  
        var _fn = function (evt) {
            evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件
            if (Rsd.isFunction(callback)) {
                callback(evt);
            }else
            {
                console.error('callback is not function object',callback)
            }
        };
        this.___gbl_fns['dragleave'][name] = _fn;
        var _dom = dom || document;
        if(_dom == document)
        {
            console.debug('为document注册onDragLeave事件时，需要主动注销事件('+name+')');
        }
        _dom.addEventListener('dragleave',_fn);
    };
    /***
     * 
     */
    this.offDragLeave = function offDragLeave(dom, name)
    { 
         var _dom = dom || document;
         var _fn = this.___gbl_fns['dragleave'][name];
         if(_fn)
         {
            _dom.removeEventListener('dragleave',_fn);
            delete this.___gbl_fns['dragleave'][name];
            console.debug('注销dragleave事件:'+name); 
         } 
    },
    /** 
    * @description onDrop拖动过程对象事件，在一个拖动过程中，到达目标对象释放鼠标键时触发此事件
    * @param dom 被拖动对象 要到达的目标对象
    * @param name 事件标识
    * @param callback 回调方法
    * */
    "use strict";
    this.onDrop = function onDrop(dom,name, callback) {
        //使用 onDrop 前必须注册onDragOver ,否则onDrop事件不会出发 
        var _fn = function (evt) {
            evt.preventDefault();//来阻止默认行为
            evt.stopPropagation();//阻止冒泡，仅保留一次事件

            if (Rsd.isFunction(callback)) {
                callback(evt);
            }else
            {
                console.error('callback is not function object',callback)
            }
        }; 
        this.___gbl_fns['drop'][name] = _fn;
        var _dom = dom|| document;
        if(_dom == document)
        {
            console.debug('为document注册onDrop事件时，需要主动注销事件('+name+')');
        }
        _dom.addEventListener('drop',_fn); 
    };
    /**
     * 
     */
    this.offDrop = function offDrop(dom, name)
    { 
         var _dom = dom || document; 
         var _fn = this.___gbl_fns['drop'][name];
         if(_fn)
         {
            _dom.removeEventListener('drop',_fn);
            delete this.___gbl_fns['drop'][name];
            console.debug('注销drop事件:'+name); 
         } 
    },
    /**
     * @public
     * @description 获取dom 计算后样式
     * @param {stirng} id dom ID值
     * @param {string} style 样式名称
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.getComputedStyle = function getComputedStyle(id, style) {

        var el = document.getElementById(id);
        var _value = null;
        if (el) {
            if (el.currentStyle) {
                _value = el.currentStyle[style];
                alert('ie:' + _value);
            } else if (window.getComputedStyle) {
                _value = window.getComputedStyle(el, null)[style];
                alert('firefox:' + _value);
            }
        }
        return _value;

    };
    /**
     * @description 预加载图片对象
     * */
    this.loadImage = function loadImage(src) {
        var img = new Image();
        img.src = src;
        return img;
    };
    /**
     * @public
     * @description 展示提示信息,可自定义按钮
     * @param {string} title 标题
     * @param {string} msg 消息内容
     * @param {array} btns 按钮集合，如：[{text:'OK',fn:function(){}}]
     * @function
     * @memberof Redjs
     * */
    this.showMessage = function showMessage(title, msg, btns) {
        var me = Rsd || this;
        var _title = "提示信息"
        var _msg = '';
        var _btns = [{ text: '确 定', fn: null }];
        if (arguments.length > 2) {
            _title = arguments[0];
            _msg = arguments[1];
            _btns = arguments[2];
        }
        if (arguments.length == 2) {

            if (Rsd.isString(arguments[1])) {
                _title = arguments[0];
                _msg = arguments[1];
            }

            if (Rsd.isArray(arguments[1])) {
                _msg = arguments[0];
                _btns = arguments[1];
            }

        }
        if (arguments.length == 1) {
            _msg = arguments[0];
        }
        if (this.isEmpty(_msg)) {
            _msg = "<空>";
            console.error('Argument msg is not allowed empty value when call method showMessage.');
        }
        
        if (Rsd.edition === 'mini') {
            window.alert('[' + title + ']' + (msg||''));
            return;
        }
        var box = me.create('Rsd.control.MessageBox', {
            title: _title,
            messageType: 'text',
            messageStyle: { fontSize: '130%' },
            message: _msg,
            buttons: _btns,
            parent: me
        });

        return box.showDialog(document.body);
    };
    /**
     *  @function
     * @memberof Redjs
     * */
    this.showHtml = function showHtml(title, html, width, height, maxLines) {
        var me = Rsd || this;
        var _title = title;
        var _html = html || '';
        if (arguments.length == 1) {
            _title = "提示信息";
            _html = arguments[0];
        }
        var _btns = [{ text: '确 定', fn: null }];
        var box = me.create('Rsd.control.MessageBox', {
            width: width || 600,
            height: height || 400,
            maxLines: maxLines,
            title: _title,
            messageType: 'html',
            message: _html,
            buttons: _btns,
            parent: me
        });
        return box.showDialog(document.body);
    };
    /**
     * @description 展示【确定】或【取消】操作的信息提示框
     * @function
     * @memberof Redjs
     * */
    this.yesOrNo = function yesOrNo(msg, callback_yes, callback_no) {
        var me = Rsd || this;
        var _title = "重要操作确认"
        var _msg = msg;
        var _btns = [{ text: '确 定', fn: callback_yes }, { text: '取 消', fn: callback_no }];

        var box = me.create('Rsd.control.MessageBox', { title: _title, message: _msg, buttons: _btns, parent: this });
        return box.showDialog(document.body);
    };


    /**
     * @public
     * @description 提示popup信息
     * @param {number} x x轴方向位置信息，以提示框居中时为位置 0
     * @param {number} y y轴方向位置信息
     * @function
     * @memberof Redjs
     * */
    this.showPopup = function showPopup(msg, x, y) {
        var me = Rsd || this;

        var _height = 50;
        var _width = 250;
        me.__popup_count = this.__popup_count || 0;

        var _popup = me.create('Rsd.container.Component', {
            border: false,
            header: false,
            width: '100%',
            height: _height,
            fixed: true,
            layout: 'fit',
            style: { zIndex: 99999, top: -50 },
            bodyCls: 'x-popup-bar',
            items: [
                {
                    xtype: 'label',
                    cls: 'x-popup-box',
                    label: { visible: false },
                    style: { left: x || 0, lineHeight: _height },
                    ctrlCls: 'x-text',
                    height: _height,
                    width: _width,
                    text: msg
                }
            ]
        });
        //_popup.doLayout();
        //_popup.header.visible = false;

        me.__popup_count++;
        //先展示后动画
        _popup.show(function () {
            _popup.animate({ opacity: 1, top: (y || 0) + me.__popup_count * 50 }, 500, function () { });
        }, 10);

        setTimeout(function () {
            me.__popup_count--;
            _popup.close({ opacity: 0, top: -50 }, 2000, 0);

        }, 3000);

    };

    /**
     * @public
     * @description 以动画的形式 将TipBox（有尖头指向）样式显示到目标（x,y）位置
     * @param string msg
     * @param int w
     * @param int h
     * @param int x
     * @param int x
     * @param int y
     * @param int position
     * */
    this.showTip = function showTip(msg, w, h, x, y, position,duration) {

        var _msg = null;
        var _w = null;
        var _h = null;
        var _x = null;
        var _y = null;
        var _position = null;

        for (var i = 0; i < arguments.length; i++) {
            if (this.isString(arguments[i]) && _msg == null) {
                _msg = arguments[i];
                continue;
            }
            if (this.isString(arguments[i]) && _position == null) {
                _position = arguments[i];
                continue;
            }
            if (this.isNumber(arguments[i]) && _w == null) {
                _w = arguments[i];
                continue;
            }
            if (this.isNumber(arguments[i]) && _h == null) {
                _h = arguments[i];
                continue;
            }
            if (this.isNumber(arguments[i]) && _x == null) {
                _x = arguments[i];
                continue;
            }
            if (this.isNumber(arguments[i]) && _y == null) {
                _y = arguments[i];
                continue;
            }

        }
        //
        this.loadClass('Rsd.control.HtmlBox');

        var _tip = this.create("Rsd.container.TipBox", {
            header: { position: _position || 'top', space: 0,style:{background:'transparent'} },
            width: _w || 200,
            height: _h || 50,
            border: false,
            style: { opacity: 0 },
            layout: { type: 'border', align: 'center' },
            listeners: {
                'click': {
                    element: 'dom',
                    fn: function (sender, evt) {

                        this.close(1000, { opacity: 0, top: _y - 200, left: _x - 200 }, 0);
                        Rsd.events.remove(Rsd, 'click', this.id);
                    }
                } 
            },
            items: [
                {
                    xtype: 'html-box',
                    label: {visible: false },
                    margin:'10 10 10 10',//距离边框10px
                    region: 'center',
                    style: { 
                        "align": 'center' ,
                        "display": "flex",
                        "justifyContent": "center", 
                        "alignItems": "center"
                    },
                    height: '100%', width: '100%', html: _msg || ''
                }
            ]
        });

        _tip.show({ left: _x == null ? 100 : _x, top: _y == null ? 50 : _y }, 1000, function () { 
            //延时加入 队列 防止click 事件 关闭显示
            Rsd.events.add(document.body, 'click', this.id, function () {
                _tip.close(1000, { opacity: 0, top: _y - 200, left: _x - 200 }, 0);
            }, true);
        });
        if(duration && Rsd.isNumber(duration))
        {
            setTimeout(function() {
                _tip.close(1000, { opacity: 0, top: _y - 200, left: _x - 200 }, 0);
            }, duration);
        }
        return _tip.id;
    };

    /**
     * @description 提示JSON数据信息
     * @function
     * @memberof Redjs
     * */
    this.alertLog = function alertLog(obj) {
        alert(Rsd.toString(obj));
    };

    /**
     * @public
     * @description 提示信息
     * @function
     * @memberof Redjs
     * */
    this.alert = function alert(title, msg, callback) {
        //console.log(arguments);
        var me = Rsd || this;
        var _title = "提示信息";
        var _msg = "";
        var _callback = null;
        if (arguments.length > 2) {
            _title = arguments[0];
            _msg = arguments[1];
            _callback = arguments[2];
        }
        if (arguments.length == 2) {
            if (Rsd.isFunction(arguments[1])) {
                _msg = arguments[0];
                _callback = arguments[1];
            }
            else {
                _title = arguments[0];
                _msg = arguments[1];
            }

        }
        if (arguments.length == 1) {
            _msg = arguments[0];
        }
        if (Rsd.edition === 'mini') {
            window.alert('[' + title + ']' + (msg||''));
            callback && callback();
            return;
        }
        var _btns = null;
        if (Rsd.isFunction(_callback)) {
            _btns = [{ text: '确 定', fn: _callback }];
        } else {
            _btns = [{ text: '确 定', fn: null }];
        }
        var box = me.create('Rsd.control.MessageBox', { 
            title: _title, 
            message: _msg, 
            messageType: ((_msg.indexOf('<') > -1 || _msg.indexOf('>') > -1) ? 'html' : 'text'), 
            buttons: _btns, 
            parent: this 
        });
        box.showDialog(document.body);
    };

    /**
     * @public
     * @description 关闭窗口
     * @function
     * @memberof Redjs
     * */
    this.closeWindow = function closeWindow() {
        var win = window.open("about:blank", "_self");
        win.close();
    };
    /*
    * */
    this.getTiming = function getTiming(ext) {
        var _timing = {
            redjsLoadStartTime: Rsd.__loadStartTime,
            redjsLoadEndTime: Rsd.__loadEndTime,
            redjsLoadTime: (Rsd.__loadEndTime || 0) - (Rsd.__loadStartTime || 0),
            redjsBuildStartTime: Rsd.__buildStartTime,
            redjsBuildEndTime: Rsd.__buildEndTime,
            redjsBuildTime: (Rsd.__buildEndTime || 0) - (Rsd.__buildStartTime || 0)
        };
        var _per = window.performance;
        if (_per && _per.timing) {

            _timing.start = _per.timing.navigationStart;
            //DNS查询耗时
            _timing.DNS = _per.timing.domainLookupEnd - _per.timing.domainLookupStart;
            //TCP链接耗时
            _timing.TCP = _per.timing.connectEnd - _per.timing.connectStart;
            //request请求耗时
            _timing.request = _per.timing.responseEnd - _per.timing.responseStart;
            //解析dom树耗时
            _timing.domComplete = _per.timing.domComplete - _per.timing.domInteractive;
            //白屏时间
            _timing.white = _per.timing.responseStart - _per.timing.navigationStart;
            //domready时间(用户可操作时间节点) ：
            _timing.domReady = _per.timing.domContentLoadedEventEnd - _per.timing.navigationStart;
            //onload时间(总下载时间)
            _timing.total = _per.timing.loadEventEnd - _per.timing.navigationStart;

            _timing.end = _per.timing.loadEventEnd;
        }

        var _data = Rsd.apply(_timing, ext || {});

        return _data;
    };

    /**
     * @public
     * @description 本地log日志，不向日志服务器发送
     * @function
     * @memberof Redjs
     * */
    this.log = function log(msg) {

        var _args = [];
        var stack = new Error().stack.split("@\n");
        stack.splice(0, 2);
        stack = stack.join("<-");
        _args.push.apply(_args, arguments);
        _args.push.apply(_args, ['call stack:' + stack + 'in class (' + (this.$className || '') + ') by object(id:' + (this.id || '') + ')']);
        console.log.apply(this, _args);

    };

    /**
     * @public
     * @description 记录调试信息
     * @param {string} msg
     * @param {object} data
     * @function
     * @memberof Redjs
     * */
    this.debug = function debug(msg, data) {
        var me = Rsd || this;
        //debugger;
        if (me.isDebug) {

            var _args = [];
            var stack = new Error().stack.split("@\n");
            stack.splice(0, 2);
            stack = stack.join("<-");
            _args.push.apply(_args, arguments);
            _args.push.apply(_args, ['call stack:' + stack + 'in class (' + (this.$className || '') + ') by object(id:' + (this.id || '') + ')']);
            console.debug.apply(this, _args);
            //发送到日志服务器
            if (me.Logger) {
                me.Logger.debug(msg, data);
            }
        }

    };


    /**
     * @public
     * @description info日志
     * @param {string} msg
     * @param {object} data
     * @function
     * @memberof Redjs
     * */
    this.info = function info(msg, data) {
        var me = Rsd || this;
        var _args = [];
        var stack = new Error().stack.split("@\n");
        stack.splice(0, 2);
        stack = stack.join("<-");
        _args.push.apply(_args, arguments);
        _args.push.apply(_args, ['call stack:' + stack + 'in class (' + (this.$className || '') + ') by object(id:' + (this.id || '') + ')']);
        console.log.apply(this, _args);
        //发送到日志服务器
        if (me.Logger) {
            me.Logger.info(msg, data);
        }
    };

    /**
         * @public
         * @description 警告日志
         * @param {string} msg
         * @param {object} data
         * */
    this.warn = function warn(msg, data) {
        var me = Rsd || this;
        var _args = [];
        var stack = new Error().stack.split("@\n");
        stack.splice(0, 2);
        stack = stack.join("<-");
        _args.push.apply(_args, arguments);
        _args.push.apply(_args, ['call stack:' + stack + 'in class (' + (this.$className || '') + ') by object(id:' + (this.id || '') + ')']);
        console.warn.apply(this, _args);
        //发送到日志服务器
        if (me.Logger) {
            me.Logger.warn(msg, data);
        }
    };

    /**
     * @public
     * @desc 调试模式下，提示错误信息，非调试模式打印到控制台；如日志接口（Rsd.Logger）存在发送日志到服务端
     * @param {string} msg
     * @param {string} url
     * @param {object} ex
     * */
    this.error = function error(msg, url, ex) {
        var _msg = msg;
        var _url = url;
        var _ex = ex;

        if (arguments.length == 1 && this.isObject(arguments[0])) {
            _msg = null;
            _url = null;
            _ex = arguments[0];
        }

        if (arguments.length == 2) {
            _url = null;
            _ex = null;
            if (this.isString(arguments[1])) {
                _url = arguments[1];
            } else {
                _ex = arguments[1];
            }

        }
        var me = Rsd || this;

        if (me.isDebug) {
            console.error(arguments);
            alert(msg + ((_ex && _ex.message) ? (':' + _ex.message) : ''));
        }
        else {
            //console.trace(arguments);
            console.error((Rsd.isEmpty(msg) ? '运行异常' : _msg) + (_url ? '(' + _url + ')' : ''));
        }
        if (_ex) {
            console.trace(_ex);
        }
        if (me.Logger) {
            me.Logger.error(_msg + '(' + _url + ')', ex);
        }
    };


    /**
     * @public
     * @description 写入会话缓存
     * @param {string} key
     * @param {string} value
     * */
    this.writeSession = function writeSession(key, value) {
        var me = Rsd || this;
        var storage = window.sessionStorage;
        if (storage) {
            storage.setItem(key, me.toString(value));
        }
    };

    /**
     * @public
     * @description 读取指定会话缓存
     * @param {string} key
     * */
    this.readSession = function readSession(key) {
        var me = Rsd || this;
        var storage = window.sessionStorage;
        var data = null;
        if (storage && storage.getItem(key) != null) {
            data = me.toJson(storage.getItem(key));
        }

        return data;
    };

    /**
     * @public
     * @description 清除指定会话缓存
     * @param {string} key
     * */
    this.removeSession = function removeSession(key) {
        var me = Rsd || this;
        var storage = window.sessionStorage;
        if (key && storage && storage.getItem(key) != null) {
            return storage.removeItem(key);
        }
    };

    /**
     * @public
     * @description 清除所有会话缓存
     * */
    this.clearSession = function clearSession() {
        window.sessionStorage.clear();
    };

    /**
     * @public
     * @description 写到本地缓存
     * @param {string} key
     * */
    this.writeLocal = function writeLocal(key, value) {
        var me = Rsd || this;
        var storage = window.localStorage;
        if (storage) {
            storage.setItem(key, me.toString(value));
        }
    };

    /**
     * @public
     * @description 读取指定本地缓存
     * @param {string} key
     * */
    this.readLocal = function readLocal(key) {
        var me = Rsd || this;
        var storage = window.localStorage;
        var data = null;
        if (storage && storage.getItem(key) != null) {
            data = me.toJson(storage.getItem(key));
        }

        return data;
    };

    /**
     * @public
     * @description 清除指定本地缓存
     * @param {string} key
     * */
    this.removeLocal = function removeLocal(key) {
        var me = Rsd || this;
        var storage = window.localStorage;

        if (key && storage && storage.getItem(key) != null) {
            return storage.removeItem(key);
        }

    };

    /**
     * @public
     * @description 清除所有本地缓存
     * */
    this.clearLocal = function clearLocal() {
        window.localStorage.clear();
    };

    /**
     * @public
     * @description 添加Cookie
     * @param {*} name 
     * @param {*} value 
     * @param {*} expiresHours  单位：小时
     */
    this.addCookie = function addCookie(name, value, expiresHours) {
        var me = Rsd || this;
        var cookieString = name + "=" + escape(value);
        //判断是否设置过期时间
        if (expiresHours > 0) {
            var date = new Date();
            date.setTime(date.getTime + expiresHours * 3600 * 1000);
            cookieString = cookieString + "; expires=" + date.toGMTString();
        }
        document.cookie = cookieString;
    };
    /**
     * @public 获取cookie，获取整个字符串，然后分割。
     * */
    this.getCookie = function getCookie(name) {
        var me = Rsd || this;
        var strCookie = document.cookie;
        var arrCookie = strCookie.split("; ");
        for (var i = 0; i < arrCookie.length; i++) {
            var arr = arrCookie[i].split("=");
            if (arr[0] == name) return arr[1];
        }
        return "";
    };
    /**
     * @public 删除cookie ，将有效期设置为过去时间
     *  * */
    this.deleteCookie = function deleteCookie(name) {
        var me = Rsd || this;
        var date = new Date();
        date.setTime(date.getTime() - 10000);
        document.cookie = name + "=null; expires=" + date.toGMTString();
    };

    /**
     * @public
     * */
    "use strict";
    this.getScript = function getScript(obj, f) {
        if (obj == undefined) {
            return "null";
        }

        var r = [];
        if (typeof obj == "string") {
            return "\'" + obj.replace(/([\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "\\t").replace(/(\')/g, "\\'") + "\'";
        }
        if (typeof obj == "object") {
            var _type = this.getType(obj);
            if (_type == '[object Array]') {
                for (var i = 0; i < obj.length; i++)
                    r.push(Rsd.getScript(obj[i], true))
                r = "[" + r.join() + "]";
            }
            if (_type == '[object Object]') {
                if (Rsd.isCreated(obj)) {
                    this.error('类型为[' + obj.xtype + ']属性定义为已创建对象,该类无法定义。');
                } else {

                    for (var i in obj) {
                        if (obj.hasOwnProperty(i)) {
                            if (typeof obj[i] != 'function') {

                                r.push((f ? '' : 'this.') + i + (f ? ':' : '=') + this.getScript(obj[i], true));
                            }
                            else {
                                //无法实现自动为未命名的方法加上方法名
                                r.push((f ? '' : 'this.') + i + (f ? ':' : '=') + obj[i].toString());
                            }
                        }

                    }
                    r = (f ? '{' : '') + r.join(f ? ',' : ';') + (f ? '}' : '');
                }

            }

            return r;
        }

        return obj.toString().replace(/\"\:/g, '":""');
    };

    /**
     * @public
     * @param className string
     * @param config object
     * @param callback function
     *
     * */
    "use strict";
    this.define = function define(className, config, callback) {

        if (!Rsd.isString(className)) {
            console.error(className);
            return;
        }
        //此处需要进行 命名规范 约束
        //console.trace(className);
        //
        var me = Rsd || this;

        me.debug('define class:' + className);

        if (me.classes[className] != null) {

            me.error('Failed to define class [' + className + '],because it has defined.');
            return;
        }

        config = config || {};
        //模拟类静态方法，在类型定义之前被执行
        if (config.static && config.static instanceof Function) {
            var _static = config.static;
            delete config.static;

            if (_static.length > 0) {
                _static(function () {
                    me.define(className, config, callback);
                });

                return;
            } else {
                _static();
            }

        }

        var _extend = config.extend;
        //delete config.extend;
        var _requires = config.requires;
        delete config.requires;
        var _xtype = config.xtype ? config.xtype : className;
        delete config.xtype;
        var _properties = {};
        var _functions = {};
        if (_extend === className) {
            this.error('Failed to define class', className, new Error('extend class don\'t allow same to className'));
            return;
        }

        if (_extend) {
            if (!me.classes[_extend]) {

                me.debug('load class ' + className + ' extend class:' + _extend);
                me.loadClass(_extend);

            }

            if (typeof me.classes[_extend] !== 'function') {
                throw new Error('define ' + className + ' Fail ,Inheritance dependency error:' + _extend + ' can not found.');
            }

        }

        var _namespace = null;
        var _namespaces = className.split('.');

        for (var i in config) {

            if (!config.hasOwnProperty(i)) {
                continue;
            }
            if (me.isFunction(config[i])) {
                _functions[i] = config[i]
                continue;
            }

            //定义对象属性
            _properties[i] = config[i];
            continue;
        }

        if (!_functions.hasOwnProperty('constructor') || !me.isFunction(_functions.constructor)) {
            _functions['constructor'] = function () {
                me.warn('Could not find function constructor in ' + this.$className + '.');
            };
        }


        for (var n in _namespaces) {

            if (!_namespaces.hasOwnProperty(n)) {
                continue;
            }

            var _script = "";


            if (!_namespaces[n]) {
                continue;
            }

            if (_namespace) {
                _namespace += '.';
            }
            else {
                _namespace = '';
            }

            _namespace += _namespaces[n];

            if (n < _namespaces.length - 1) {
                _script = 'if(!' + _namespace + '){' + _namespace + '={};}; ';
            }
            else {

                _script = '' + _namespace + ' =';
                _script += ' function ' + _namespaces[n] + '(_config){';
                //动态原型技术
                if (_extend) {
                    _script += 'this.__proto__.__proto__ = new ' + (_extend || 'Object') + '(false);';
                }

                //属性初始化
                _script += me.getScript(_properties) + ';';
                //_script += 'console.log(this.constructor);';
                _script += 'this.constructor.call(this,_config);';
                _script += 'if(_config && Rsd.isFunction(this.init)){ this.init.call(this);}';
                _script += 'return this;};';
                _script += 'Rsd.classes[\'' + className + '\']=' + _namespace + ';';

            }

            window.eval(_script);
        }

        var _depClass = _extend;

        for (var p in _functions) {
            me.classes[className].prototype[p] = _functions[p];
        }

        me.classes[className].prototype.$className = className;
        me.classes[className].prototype.xtype = _xtype;
        me.classes[className].prototype.dependencies = new Array();
        me.classes[className].prototype.dependencies[_xtype] = className;

        if (_depClass) {
            me.classes[className].prototype.dependencies[me.types[_depClass]] = _depClass;
        }

        var __isLoading = me.isLoading();
        for (var i in _requires) {
            //debugger;
            if (!_requires.hasOwnProperty(i)) {
                continue;
            }
            _depClass = _requires[i];
            if (_depClass.length == 0) {
                continue;
            }
            //框架加载中， 不加载依赖项
            if (__isLoading === false && !me.classes[_depClass]) {

                me.debug('load class ' + className + ' dependent :' + _extend);
                this.loadClass(_depClass);
            }

            this.classes[className].prototype.dependencies[me.types[_depClass]] = _depClass;
        }

        me.types[className] = _xtype

        if (me.xtypes.hasOwnProperty(_xtype)) {
            me.error('The xtype \'' + _xtype + '\' of class [' + className + '] is exist.');
        }
        else {
            me.xtypes[_xtype] = className;
        }


        //_properties 属性定义
        var _t = _extend ? new me.classes[_extend]() : {};

        var _script = '';
        for (var p in _properties) {
            if (_t.hasProperty && _t.hasProperty(p)) {

                /** @bug 父类对象属性不能重复定，否则会覆盖父类功能*/
                //console.log('[' + className + '] prototype has property \'' + p + '\',program continue.');
                continue;
            }

            //var enumerable = true;
            //console.log('==>define [' + className + '] property :' + p);
            _script += 'Rsd.defineProperty(Rsd.classes[\'' + className + '\'],\'' + p + '\',function __getter(){return this[\'__' + p + '\'];},function __setter(value){ this[\'__' + p + '\'] = value;},true);';
        }

        //console.log(_script);
        window.eval(_script);

        if (me.isFunction(callback)) {
            callback.call(me, me.classes[className]);
        }

        me.debug('define class:' + className + ' end.');
    };

    /**
     * @public
     * @description 属性定义,如果属性存在，将跳过
     * */
    "use strict";
    this.defineProperty = function defineProperty(type, propertyName, getter, setter, enumerable) {

        var _type = this.isString(type) ? this.classes[type] : type;

        if (!_type) {
            return;
        }
        if (_type.prototype.hasOwnProperty(propertyName)) {
            console.warn('Property [' + propertyName + '] is exsit in ' + _type.prototype.$className);
            return;
        }

        if (Object.defineProperty) {
            var _obj = { enumerable: enumerable, configurable: true };//{value:defaultValue,writable:true,enumerable: enumerable,configurable:true};
            if (getter) {
                _obj['get'] = getter;
            }
            if (setter) {
                _obj['set'] = setter;
            }
            Object.defineProperty(_type.prototype, propertyName, _obj);
        } else {
            if (getter && Object.prototype.__defineGetter__) {
                _type.prototype.__defineGetter__(propertyName, getter);
            }
            if (setter && Object.prototype.__defineSetter__) {
                _type.prototype.__defineSetter__(propertyName, setter);
            }
        }
    };

    /**
     *@public
     * */
    this.setDomToken = function setDomToken(flag, value) {
        this.domFlag = flag;
        this.domFlagValue = value;
    };

    /**
     * @public
     * @description 单列和需要缓存的对象会存入Rsd.objects对象中。
     * */
    "use strict";
    this.create = function create(className, config) {
        var me = Rsd || this;
        //debugger;
        if (!className || className == '') {
            throw new Error('call Rsd.create function argument \'className\' is null. ');
        }
        if (me.singletons[className]) {
            return me.objects[me.singletons[className]];
        }

        me.debug('create object:' + className);

        me.loadClass(className);

        if (me.classes[className] == null) {
            throw new Error('class \'' + className + '\' not found.');
        }

        var _obj = new me.classes[className](config);

        if (_obj.events) {
            _obj.events.fire(_obj, 'aftercreate', {});
        }


        //debugger;
        if (_obj.singleton || _obj.cache) {
            me.objects[_obj.id] = _obj;
        }
        if (_obj.singleton) {
            me.singletons[className] = _obj.id;
        }
        me.debug('return object:' + _obj.id);
        return _obj;
    };
    /**
     * @public
     * @param xtype
     * @param config
     *
     * */
    "use strict";
    this.widget = function widget(xtype, config) {

        var _xtype = xtype;
        var _className = null;
        var _config = config || {};


        if (this.isString(xtype)) {
            _xtype = xtype;
        }

        if (this.isObject(xtype)) {

            _config = xtype;
            _xtype = _config['xtype'];
            delete _config['xtype'];
        }
        if (this.isEmpty(_xtype)) {
            console.error("call Redjs.widget of argument \'xtype\' is null.'", arguments);
            throw new Error('call Redjs.widget of argument \'xtype\' is null.');

        }
        _className = this.xtypes[_xtype];
        if (!_className) {
              
            _className = this.xtypes['label'];
            console.error('widget control error: not found the mapping className of xtype \'' + _xtype + '\'.',config);
        }

        return this.create(_className, _config);
    };

    /**
     *@public
     * */
    this.isCreated = function isCreated(obj) {
        return obj && this.isFunction(obj.isCreated) && obj.isCreated();
    };

    /**
     * @private
     * */
    this.getId = function getId(prefix) {

        this.__id++;
        var _id = prefix.replaceByRegExp('[-]', '_') + '_' + this.__id;
        while (document.getElementById(_id)) {
            this.__id++;
        }
        return _id;
    };

    /**
     * @public
     * @description zIndex  + offset
     * @return number
     * */
    this.getZIndex = function getZIndex(offset) {
        return this.__zIndex + offset;
    };

    /**
     * @public
     * @description 对象克隆
     * */
    this.clone = function clone(obj) {
        var o;

        switch (typeof obj) {
            case 'undefined':
                break;
            case 'string':
                o = obj + '';
                break;
            case 'number':
                o = obj - 0;
                break;
            case 'boolean':
                o = obj;
                break;
            case 'object':
                if (obj === null) {
                    o = null;
                    break;
                }
                if (obj instanceof Array) {
                    o = [];
                    for (var i = 0, len = obj.length; i < len; i++) {
                        o.push(clone(obj[i]));
                    }
                } else {

                    if (obj instanceof Node) {
                        o = obj.cloneNode(true);
                        break;
                    }

                    o = {};

                    for (var k in obj) {
                        o[k] = clone(obj[k]);
                    }
                }
                break;
            default:
                o = obj;
                break;
        }
        return o;
    };

    /**
     @description 将参数config合并到obj中
     * */
    this.apply = function apply(obj, config, deep) {

        var _deep = deep || 1;
        _deep = _deep < 1 ? 1 : _deep;
         
        var _obj = obj||config;
        var _config = config||null;
        if (obj && config && this.isType(obj, Object) && this.isObject(config)) {

            for (var i in config) {
                try {
                    //delete  obj[i];
                    if (_deep == 1) {
                        obj[i] = config[i];
                        continue;
                    }
                    //console.log(i + '--' + _deep);
                    if (obj[i] && config[i] && this.isType(obj[i], Object) && this.isObject(config[i])) {
                        //console.log('1111'); 
                        // console.log(this.toString(obj[i])); 
                        // console.log(this.toString(config[i]));
                        this.apply(obj[i], config[i], _deep - 1)
                    } else {

                        obj[i] = config[i];
                    }

                } catch (ex) {
                    this.error('Run function Redjs.apply fail', 'Rsd', ex);
                }
            }
        }

        return _obj;
    };

    /**
     * @public
     * */
    this.loadClass = function loadClass(className, callback) {

        if (!this.isString(className)) {
            console.error(className);
            return;
        }
        var me = Rsd || this;

        if (!me.isNullOrUndefined(me.classes[className])) {

            return;
        }
 
        if(callback)
        {
            if (me.classesCallback[className]) {
                me.classesCallback[className].push(callback);
                return;
            }
            me.classesCallback[className] = [callback];
        }
         
        me.debug('Load class: ' + className);

        var _url = className;
        var _flag = true;

        //从插件中获取
        if (_flag && me.app && me.app.plugins) {

            var _c_name = className.substring(0, className.lastIndexOf('.'));
            var _p = me.app.getPlugin(_c_name);
            while (_p == undefined || _p == null) {
                var i = _c_name.lastIndexOf('.');
                if (i < 0) {
                    break;
                }
                _c_name = _c_name.substring(0, i);
                _p = me.app.getPlugin(_c_name);
            }
            if (_p) {
                _url = _url.replace(_p.name, '');
                _url = _url.replace("Rsd", me.getRedjsHost());
                _url = _p.path + '/' + _url.replaceByRegExp('[.]', '/') + '.js';
                _url += '?t=' + (_p.isDebug ? new Date().getTime() : _p.pluginDate);

                _flag = false;
            }

        }

        if (_flag && me.app && className.startWith(me.app.appName)) {

            _url = _url.replace(me.app.appName, '');
            _url = _url.replace("Rsd", me.getRedjsHost());
            _url = me.app.appFolder + '/' + _url.replaceByRegExp('[.]', '/') + '.js';
            _url += '?t=' + (me.app.isDebug ? new Date().getTime() : this.app.appDate);

            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;

            _flag = false;
        }



        if (_flag) {
            _url = _url.replaceByRegExp('[.]', '/') + '.js';
            if (me.isAgentHost()) {
                _url = _url.replace("Rsd", "");
                if (_url.startWith('/')) {
                    _url = _url.substr(1);
                }

                _url = me.getRedjsHost() + this.utf8ToBase64(_url + '?t=' + (me.isDebug ? new Date().getTime() : this.timestamp));
            }
            else {
                _url = _url.replace("Rsd", me.getRedjsHost());
                _url += '?t=' + (me.isDebug ? new Date().getTime() : this.timestamp);
            }

        }

        me.loadScriptFile(_url, function (response, status) {

            if (me.classes[className]) {

                me.debug('Load class: ' + className + ' end.');

                var _list = me.classesCallback[className];
                for (var i in _list) {
                    me.callFunction(me, _list[i]);
                }

            }
            else {

                me.error(className + ' can not found.Please check class name is case upper or lower.');
            }

        });

    }

    /**
     *@public
     *@desc 用<script> 加载标签 pugins 目录下资源或第三方资源,可以避免异步加载方式失败。
     * */
    this.loadPlugin = function loadPlugin(url, callback) {

        var me = Rsd || this;
        var name = url;
        if (name == undefined || name == null || name == '') {
            return;
        }
        var src = null;
        if (name.toLowerCase().startWith('http://') || name.toLowerCase().startWith('https://')) {
            src = name;
        } else {
            src = me.getRedjsUrl(name);
        }

        me.debug('Load plugin:' + src);

        var el = document.createElement('script');
        el.type = "text/javascript"
        el.src = src;
        el.onload = el.onreadystatechange = function onready() {

            if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
                if (me.isFunction(callback)) callback.call();
                // Handle memory leak in IE
                el.onload = el.onreadystatechange = null;
            }
        };

        document.getElementsByTagName('head')[0].appendChild(el);

    };
    /**
         * @public
         * @description 用ajax【同步加载】脚本文件，加载到本地后并执行 不用用于加载jquery
         * @param {string} url 脚本地址
         * @param {string} callback 加载执行完成回调函数
         * */
    this.loadFile = function loadFile(url, callback) {

        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else {

            if (_url.startWith('/')) {
                _url = _url.substr(1);
            }

            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;
        }

        me.debug('Load file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } 
        if(xhr==null && typeof ActiveXObject !=='undefined')  {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }
        if(xhr==null)
        {
            return;
        }
        xhr.open('GET', _url, false);//同步加载
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
        //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
        xhr.send(null);
        //同步 不出发 onload  onreadystatechange 事件
        //xhr.onload = function(){};
        //xhr.onreadystatechange = function(){};

        if (xhr.readyState == 4 && xhr.status == 200) {

            var str = xhr.responseText;

            if (me.isFunction(callback)) {
                callback.apply(me, [str]);
            }

        }
        else {
            me.error('Failed to load file resource: ' + _url + "(" + xhr.responseText + ")");
        }

        me.debug('Load file: ' + _url + ' end.');
    };
    /**
     * @public
     * @description 用ajax【同步加载】脚本文件，加载到本地后并执行 不用用于加载jquery
     * @param {string} url 脚本地址
     * @param {string} callback 加载执行完成回调函数
     * */
    this.loadScriptFile = function loadScriptFile(url, callback) {

        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else {

            if (_url.startWith('/')) {
                _url = _url.substr(1);
            }

            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;
        }

        me.debug('Load script file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } 
        if(xhr==null && typeof ActiveXObject !=='undefined')  {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }
        if(xhr==null)
        {
            return;
        }
        xhr.open('GET', _url, false);//同步加载
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
        //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
        xhr.send(null);
        //同步 不出发 onload  onreadystatechange 事件
        //xhr.onload = function(){};
        //xhr.onreadystatechange = function(){};

        if (xhr.readyState == 4 && xhr.status == 200) {

            var str = xhr.responseText;

            try {
                window["eval"].call(window, str);
            }
            catch (e) {
                console.error('eval', e);
                console.debug(str);
                try {
                    window.eval(str);
                }
                catch (e1) {
                    console.error('load javascript file [' + url + '] successed,script execute error in function loadScriptFile.');
                    console.error(e1);
                }

            }


            if (me.isFunction(callback)) {
                callback.apply(me, [str]);
            }

        }
        else {
            me.error('Failed to load script file resource: ' + _url + "(" + xhr.responseText + ")");
        }

        me.debug('Load script file: ' + _url + ' end.');
    };
    /**
     * @public
     * @description 用ajax【异步加载】json文件，加载到本地后并序列化json 不用用于加载jquery
     * @param {string} url 脚本地址
     * @param {string} callback 加载执行完成回调函数 
    */
    this.loadJsonFile = function loadJsonFile(url, callback) {
        if (this.isEmpty(url)) {
            console.error("url is empty");
            return;
        }
        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else {
            if (_url.startWith('/')) {
                _url = _url.substr(1);
            }

            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;
        }

        me.debug('Load json file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } 
        if(xhr==null && typeof ActiveXObject !=='undefined')  {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }
        if(xhr==null)
        {
            return;
        }
        try {
            xhr.open('GET', _url, true);//异步加载
            xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
            //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
            xhr.send(null);
            xhr.onload = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {

                    var str = xhr.responseText;

                    try {
                        var json = me.toJson(str);
                        if (me.isFunction(callback)) {
                            callback.apply(me, [json]);
                        }
                    }
                    catch (e) {
                        console.error('load json file [' + url + '] successed.');
                        console.error(e);
                    }
                }
                else {
                    me.error('Failed to load json file resource: ' + _url + "(" + xhr.responseText + ")");
                }

                me.debug('Load json file: ' + _url + ' end.');
            };
            xhr.onerror = function (evt) {
                console.error('load json file error', evt);
            }
            //xhr.onreadystatechange = function(){ };
        }
        catch (err) {
            console.error('load json file occured exception', err);
        }
    },
        /**
         *@public
         * */
        this.loadCssFile = function loadCssFile(url) {
            if (this.isEmpty(url)) {
                console.error("url is empty");
                return;
            }

            var head = document.getElementsByTagName('HEAD').item(0);
            var style = document.createElement('link');
            style.href = url;
            style.rel = 'stylesheet';
            style.type = 'text/css';
            head.appendChild(style);
        };

    /**
     * @public
     * */
    this.gbkToUTF8 = function gbkToUTF8(wide) {
        var c, s;
        var enc = "";
        var i = 0;
        while (i < wide.length) {
            c = wide.charCodeAt(i++);
            // handle UTF-16 surrogates
            if (c >= 0xDC00 && c < 0xE000) continue;
            if (c >= 0xD800 && c < 0xDC00) {
                if (i >= wide.length) continue;
                s = wide.charCodeAt(i++);
                if (s < 0xDC00 || c >= 0xDE00) continue;
                c = ((c - 0xD800) << 10) + (s - 0xDC00) + 0x10000;
            }
            // output value
            if (c < 0x80) enc += String.fromCharCode(c);
            else if (c < 0x800) enc += String.fromCharCode(0xC0 + (c >> 6), 0x80 + (c & 0x3F));
            else if (c < 0x10000) enc += String.fromCharCode(0xE0 + (c >> 12), 0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
            else enc += String.fromCharCode(0xF0 + (c >> 18), 0x80 + (c >> 12 & 0x3F), 0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
        }
        return enc;
    };

    /**
     *
     * @public
     * */
    this.unicodeToUTF8 = function unicodeToUTF8(str) {
        if (null == str)
            return null;
        var strUni = String(str);
        var strUTF8 = String();
        for (var i = 0; i < strUni.length; i++) {
            var wchr = strUni.charCodeAt(i);
            if (wchr < 0x80) {
                strUTF8 += strUni.charAt(i);
            }
            else if (wchr < 0x800) {
                var chr1 = wchr & 0xff;
                var chr2 = (wchr >> 8) & 0xff;
                strUTF8 += String.fromCharCode(0xC0 | (chr2 << 2) | ((chr1 >> 6) & 0x3));
                strUTF8 += String.fromCharCode(0x80 | (chr1 & 0x3F));
            }
            else {
                var chr1 = wchr & 0xff;
                var chr2 = (wchr >> 8) & 0xff;
                strUTF8 += String.fromCharCode(0xE0 | (chr2 >> 4));
                strUTF8 += String.fromCharCode(0x80 | ((chr2 << 2) & 0x3C) | ((chr1 >> 6) & 0x3));
                strUTF8 += String.fromCharCode(0x80 | (chr1 & 0x3F));
            }
        }
        return strUTF8;
    };

    /*
    *
    * */
    this.utf8ToBase64 = function utf8ToBase64(str) {
        return window.btoa&&window.btoa(unescape(encodeURIComponent(str)));
    }

    /*
    *
    * */
    this.base64ToUTF8 = function base64ToUTF8(str) {

        return decodeURIComponent(escape(window.atob&&window.atob(str)));
    }


    /**
     * @public
     * @description  获取屏幕设备像素值（1英寸正方形区域（长*宽）像素）
     * */
    this.getDPI = function getDPI() {
        if (this.__dpi) {
            return this.__dpi;
        }
        var _width, _height;
        if (window.screen.deviceXDPI) {
            _width = window.screen.deviceXDPI;
            _height = window.screen.deviceYDPI;
        }
        else {
            var tmpNode = document.createElement("DIV");
            tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden;";
            document.body.appendChild(tmpNode);
            _width = parseInt(tmpNode.offsetWidth);
            _height = parseInt(tmpNode.offsetHeight);
            tmpNode.parentNode.removeChild(tmpNode);
            //console.log(_width,_height);
        }
        this.__dpi = { width: _width, height: _height };
        return this.__dpi;
    };
    /**
     * @description  将一个块区域的长宽的值（px）转换为以mm为计算单位的值
     * @param {number} width
     * @param {number} height
     * @param {number} scale 缩放比例 参考: window.devicePixelRatio;
     * @returns {object} {width:100,height:100}
     **/
    this.pxToMm = function pxToMm(width, height, scale) {

        var dpi = this.getDPI();
        var _scale = scale || 1;
        var inch_w = (width || 0) / (dpi.width * _scale);
        var _width = inch_w * 25.4;

        var inch_h = (height || 0) / (dpi.height * _scale);
        var _height = inch_h * 25.4;

        return { width: _width, height: _height };
    };
    /**
     * @description  将一个块区域的长宽的值（mm）转换为以px为计算单位的值
     * @param {number} width
     * @param {number} height
     * @param {number} scale 缩放比例 参考: window.devicePixelRatio;
     * @returns {object} {width:100,height:100}
     */
    this.mmToPx = function mmToPx(width, height, scale) {

        var dpi = this.getDPI();
        var inch_w = (width || 0) / 25.4;
        var inch_h = (height || 0) / 25.4;
        var _scale = scale || 1;
        var _width = inch_w * dpi.width * _scale;
        var _height = inch_h * dpi.height * _scale;

        return { width: _width, height: _height };;
    };
    /**
     * 
     * @param {*} url 
     * @param {*} callback 
     */
    this.loadTemplateFile = function loadTemplateFile(url, callback) {

        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else {

            if (_url.startWith('/')) {
                _url = _url.substr(1);
            }

        }

        me.debug('Load file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } 
        if(xhr==null && typeof ActiveXObject !=='undefined')  {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }
        if(xhr==null)
        {
            return;
        }
        xhr.open('GET', _url, false);
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
        //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
        xhr.send(null);

        if (xhr.readyState == 4 && xhr.status == 200) {

            var str = xhr.responseText;

            if (me.isFunction(callback)) callback.call(me, str);

        } else {
            me.error('Failed to load resource: ' + _url + "(" + xhr.responseText + ")");
        }

        me.debug('Load file: ' + _url + ' end.');

    };

    /***
     * @description
     * 编译模板 或 编译+渲染模板;模板字符串为变量添加data.前缀可以防止 with作用域被突破 
     * Name is {{=data.Name||'sa'}} , Age is {{=Age}}, from  <%=data.City%> , <%=Country%>
     */
    this.template = function template(tplStr, data, caller) {

        if(this.isString(tplStr))
        {
            var rs = this.compile(tplStr);
            var _fn = rs.fn;
            //var _list = rs.list;
            var _data = data || {};
            //console.log(_data)
            return _data ? _fn.call(caller || this, _data) : _fn;
        }

        if(this.isObject(tplStr))
        {
            var _data = {};
            for(var p in tplStr)
            {
                _data[p] = this.template(tplStr[p],data,caller);
            }
            return _data;
        }

        if(this.isArray(tplStr))
        {
            var _arr = [];
            for(var i in tplStr)
            {
                _arr.push(this.template(tplStr[i],data,caller));
            }
            return _arr;
        }
       
        return tplStr;
    };

    /**
     * 模板编译 并缓存 方法 ,返回编译后的方法
     * @param {*} tplStr 模板字符串
     * @param {*} caseno 引擎方案：1，2 or 3
     * @returns {fn:function,list:['name','age']}
     */
    this.compile = function compile(tplStr, caseno) {
        var _fn = null;
        var _list = [];
        //模板编译引擎 方案一 
        if (Rsd.isEmpty(caseno) || caseno == 1) {
            //两种正则表达式匹配模式： 
            // interpolate：取值变量，需要在其位置替换出相应的值==>可扩展成支持多种模式：{{name}},<%=name%>
            // evaluate：代码，执行代码语句
            var _interpolate = /<%=(.+?)%>/g;
            var _interpolate1 = /{{=(.+?)}}/g;
            var _evaluate = /<%(.+?)%>/g;
            var _evaluate1 = /{{(.+?)}}/g;

            var regStr = _interpolate.source + "|" + _interpolate1.source + "|" + _evaluate.source + "|" + _evaluate1.source + "|$"
            //正则表达式组
            //var matcher = new RegExp(`${interpolate.source}|${interpolate1.source}|${evaluate.source}|${evaluate1.source}|$`, 'g' );
            var matcher = new RegExp(regStr, 'g');

            var index = 0
            var _code = '';
            //需要处理的特殊字符
            var escapes = {
                '\\': '\\',
                '\n': 'n',
                '\r': 'r',
                '\u2028': 'u2028',
                '\u2029': 'u2029',
                "'": "'",
            };

            var escapeReg = /[\n\r\u2028\u2029\\']/g;
            //特殊字符转义 处理
            var escapeChar = function (match) { return '\\' + escapes[match]; };
            var str = tplStr || '';
            if (!Rsd.isString(str)) {
                console.error("模版字符串错误，不是字符类型：" + str);
                return null;
            }


            str.replace(matcher, function (match, interpolate, interpolate1, evaluate, evaluate1, offset) {

                // match 匹配到对象   console.log(match);
                // console.log(interpolate);
                // console.log(interpolate);
                // 正则对象的lastIndex属性只有在开启g标志且在regexp.exec和regexp.test方法有效，指定下次匹配的开始位置，此属性可读可写，
                // 如果方法没找到任何匹配则自动将它设置0，而在这里，用index来模拟lastIndex的作用

                // 另外需要注意，matcher最后的`$`目的是匹配字符串结束位置，从而得到结束位置的offset，
                // 当`$`发生匹配时，match是空字符串，因为`$`是零宽断言，确实发生匹配但是没有匹配内容，故返回空字符串

                //特殊字符处理
                // 使用slice方法取子字符串的副本，确保str保持不变
                // 将本次匹配到的<%=xxx%>或<%xxx%>之前的文本进行特殊字符文本化
                var line = str.slice(index, offset).replace(escapeReg, escapeChar);
                _code += line;

                // 记录下次replace匹配的开始位置
                index = offset + match.length;
 
                // 进行替换
                // 这里巧妙利用了正则表达式的 捕获分组 和 或运算
                // `/part1(group1)|part2(group2)|part3/g`这是上面matcher的结构，
                // 由于或运算的存在，只要三者之一匹配成功，整个正则表达式匹配成功，就会执行replace的回调函数，
                // 由于group1和group2必然要存在（因为它们写在正则表达式里面），
                // 那么其中某一个就得是undefined，如果是part3发生的匹配，那么group1和group2都是undefined

                //console.log(interpolate,interpolate1,evaluate,evaluate1);

                //匹配到对象属性格式0:<%=Name%> 
                if (interpolate != undefined) {
                    _list.push(interpolate);
                    _code += '\' + (' + interpolate + '===undefined?\'\':' + interpolate + ') + \'';
                }
                //匹配到对象属性格式1:{{=Name}} 
                if (interpolate1 != undefined) {
                    _list.push(interpolate1);
                    _code += '\' + (' + interpolate1 + '===undefined?\'\':' + interpolate1 + ') + \'';
                }

                //要替换的代码格式0:<%code%> 
                if (evaluate != undefined) {
                    _code += '\'; ' + evaluate + ' p+=\'';
                    //p += '; p+=\'' + evaluate + '\'';
                }
                //要替换的代码格式1:{{code}} 
                if (evaluate1 != undefined) {
                    _code += '\'; ' + evaluate1 + ' p+=\'';
                    //_code += '; p+=\'' + evaluate1 + '\'';
                }

                //把匹配到的字符串原封不动地还回去，确保str保持不变
                return match;

            });

            // 给p拼上头部和尾部的代码, with 语法影响性能，with 在严格模式下，无法使用，且存在作用域干扰
            var code = "var p = ''; try{with(data){p+='" + _code + "';}}catch(err){console.error('模板渲染失败：'+ err.message);console.log(JSON.stringify({data}));} return p;";

            //console.debug("render code is：",code);
            // 创建模板渲染函数
            try {
                _fn = new Function('data', code);
            }
            catch (err) {
                console.error('创建模板渲染方法失败',err.message);
                _fn = function () {
                    console.debug("模板参考格式：{{=name}}或<%=name%>");
                }
                console.error("模板编译失败：" + code);

            }
  
            return { fn: _fn, list: _list };
        }

        //模板编译引擎 方案二 ，不使用with
        if (caseno == 2) {


            //两种正则表达式匹配模式： 
            // interpolate：取值变量，需要在其位置替换出相应的值：{{=name}},<%=name%>
            // evaluate：代码，执行代码语句 {{code}},<%code%>
            var interpolate = /<%=(.+?)%>/g;
            var interpolate1 = /{{=(.+?)}}/g;
            var evaluate = /<%(.+?)%>/g;
            var evaluate1 = /{{(.+?)}}/g;

            var regStr = interpolate.source + "|" + interpolate1.source + "|" + evaluate.source + "|" + evaluate1.source + "|$"
            //正则表达式组
            //var matcher = new RegExp(`${interpolate.source}|${interpolate1.source}|${evaluate.source}|${evaluate1.source}|$`, 'g' );
            var matcher = new RegExp(regStr, 'g');

            var index = 0
            var _code = '';
            //需要处理的特殊字符
            var escapes = {
                '\n': 'n',
                '\r': 'r',
                '\u2028': 'u2028',
                '\u2029': 'u2029',
                '\\': '\\',
                "'": "'",
            };

            var escapeReg = /[\n\r\u2028\u2029\\']/g;
            //特殊字符转义 处理
            var escapeChar = function (match) { return '\\' + escapes[match]; };
            var str = tplStr || '';

            str.replace(matcher, function (match, interpolate, interpolate1, evaluate, evaluate1, offset) {


                var line = str.slice(index, offset).replace(escapeReg, escapeChar);
                _code += line;

                // 记录下次replace匹配的开始位置
                index = offset + match.length;
                //console.log(interpolate,interpolate1,evaluate,evaluate1);

                //属性格式0:<%=Name%> 匹配到对象属性
                if (interpolate != undefined) {
                    _list.push(interpolate);
                    _code += '\' + (data[\'' + interpolate + '\']||\'\') + \'';
                }
                //属性格式1:{{=Name}} 匹配到对象属性
                if (interpolate1 != undefined) {
                    _list.push(interpolate1);
                    _code += '\' + (data[\'' + interpolate1 + '\']||\'\') + \'';
                }
                //代码格式0:<%Name%> 要替换的代码
                if (evaluate != undefined) {
                    _code += '\'; ' + evaluate + ' p+=\'';
                    //p += '; p+=\'' + evaluate + '\'';
                }
                //代码格式1:{{Name}} 要替换的代码
                if (evaluate1 != undefined) {
                    _code += '; p+=\'' + evaluate1 + '\'';
                }

                // 把匹配到的字符串原封不动地还回去，确保str保持不变
                return match;

            });
            var code = "var p = ''; try{ p+='" + _code + "';}catch(err){console.error(err);console.log(data);} return p;";
            // 创建模板渲染函数
            try {
                _fn = new Function('data', code);
            }
            catch (err) {
                console.error(err);
                _fn = function () {
                    console.debug("模板参考格式：{{=name}}或<%=name%>");
                }
                console.error("模板编译失败：" + code);

            }

            //console.log(code);

            return { fn: _fn, list: _list };
        }

        //模版编译引擎 方案三 
        {
            //模版编译引擎 方案三 , 不支持格式：{{}} 和 {{=user.name}} 仅支持<%user.name%>
            var str = tplStr || '';

            _fn = new Function(
                'data',
                'var p=[],print=function(){p.push.apply(p,arguments);};' +
                "with(data){p.push('" +
                str
                    .replace(/[\r\t\n]/g, ' ')
                    .split('<%')
                    .join('\t')
                    .replace(/((^|%>)[^\t]*)'/g, '$1\r')
                    .replace(/\t=(.*?)%>/g, "',$1,'")
                    .split('\t')
                    .join("');")
                    .split('%>')
                    .join("p.push('")
                    .split('\r')
                    .join("\\'") +
                "');}return p.join('');"
            );

            return { fn: _fn, list: _list };
        }
    };

    /**
     * @public
     * @description  格式化模版字符串
     * */
    this.formatTemplateString = function formatTemplateString(str, data, caller) {

        if (this.isEmpty(str)) {
            return "";
        }
        var _str = str;

        if (str.indexOf("#=") > -1 && str.indexOf("=#") > -1) {
            _str = str.replace("#=", "{{=").replace("=#", "}}");
            console.warn('不建议使用的模板语法格式：#=name=#,请使用{{=name}}格式替代');
            console.warn(str);
        }

        var html = this.template(_str, data, caller);

        return html;
    },

        /**
         * @public
         * @description 获取App    
        * */
    this.getApp = function getApp() {
            return this.app;
    };
    /**
     * 添加App属性
     */
    this.setAppConfig = function setAppConfig(name, value) {
        if (!this.isEmpty(this.app)) {
            this.app[name] = value;
        }
    },

        /**
        * 获取APP配置项，即app属性值
        */
        this.getAppConfig = function getAppConfig(name) {
            if (this.isEmpty(this.app)) {
                return '';
            }
            return this.app[name]
        };

    /**
     * @public
     * @description 获取App Id
     * */
    this.getAppId = function getAppId() {

        if (this.isEmpty(this.app)) {
            return '';
        }
        return this.app.appId || window.Rsd.projectId;
    };
    /**
     * @public
     * @description 获取App token
     * */
    this.getAppToken = function getAppToken() {

        if (this.isEmpty(this.app)) {
            return '';
        }

        //console.log('getAppToken',this.app.appToken);
        return this.app.appToken;
    };
    /**
     * 设置token 变量名称，在 缓存token 时 ，作为键值名称使用
     * @param {*} name 
     */
    this.setAppTokenName = function setAppTokenName(name) {
        this.tokenName = name || 'token';
    }
    /**
     * @public
     * @description 设置token
     * */
    this.setAppToken = function setAppToken(token, name) {

        this.tokenName = name || 'token';
        if (this.isEmpty(this.app) && this.getApp) {
            this.app = this.getApp();
        }
        if (this.app) {
            this.app.appToken = token;
        }

        //console.log('setAppToken',this.app.appToken);
    };
    /**
     * 
     */
    this.getAppAuthorization = function getAppAuthorization() {
        if (this.isEmpty(this.app)) {
            return '';
        }
        return this.app.authorization;
    };
    /**
     * @public
     * */
    this.checkToken = function checkToken() {

    };

    /**
     * @public
     * @param config 将被合并到Ajax对象上
     * @param callback 回调函数
     * */
    this.httpRequest = function httpRequest(config, data, callback) {
        var _c = null;
        var _d = null;
        var _fn = null;
        for (var i in arguments) {
            if (_c == null && this.isString(arguments[i])) {
                _c = { url: arguments[i] };
                continue;
            }
            if (_c == null && this.isObject(arguments[i])) {
                _c = arguments[i];
                continue;
            }
            if (_d == null && this.isObject(arguments[i])) {
                _d = arguments[i];
                continue;
            }
            if (_fn == null && this.isFunction(arguments[i])) {
                _fn = arguments[i];
                continue;
            }
        }
        if (_fn == null) {

            _fn = void (0);
        }
        var _ajax = this.create('Rsd.data.Ajax', this.apply({ token: Rsd.getAppToken(), appId: Rsd.getAppId() }, _c));

        return _ajax.request(_d, _fn);
    };

    /**
     * 
     */
    this.httpGet = function httpGet(config, callback) {
        if (Rsd.isString(config)) {
            return this.httpRequest({ url: config, method: 'get' }, null, callback);
        }
        config.method = 'GET';
        return this.httpRequest(config, null, callback)
    };
    /**
     * 
     */
    this.httpPost = function httpPost(config, data, callback) {
        if (Rsd.isString(config)) {
            return this.httpRequest({ url: config, method: 'post' }, data, callback);
        }
        config.method = 'POST';
        return this.httpRequest(config, data, callback)
    };


    /**
     * 上传文件
     * @param serviceName 服务名称
     * @param {*} data 包含File对象的数据
     * @param {*} callback 回调方法
     */
    this.postFile = function postFile(serviceName, data, callback, msg) {
        var _file = this.create('Rsd.data.File', {
            progressChanged: function (evt) {
                console.log('上传进度：', { value: evt.loaded, max: evt.total });
            }
        });

        return _file.upload(serviceName, data, callback, msg);
    };
    /**
     * 将字符串 通过文件的方式上传到服务器
     * @param {*} serviceName 服务名称
     * @param {*} content 文件内容 字符串
     * @param {*} fileName 文件名
     * @param {*} fileType  文件类型
     * @param {*} callback 回调方法
     * @returns 
     */
    this.postFileFromString = function postFileFromString(serviceName, content, fileName, fileType, callback) {
        var _file = this.stringToFile(content, fileType, fileName);
        var _ajax = this.create('Rsd.data.File', {});
        return _ajax.upload(serviceName, { file: _file }, callback);
    };

    /**
    *
    * @description 提交表单数据
    * */
    this.submitForm = function submitForm(form, callback) {

        if (form && form.action) {
            var formData = new FormData(form);
            var _ajax = this.create('Rsd.data.Ajax', { token: Rsd.getAppToken(), appId: Rsd.getAppId(), contentType: "application/x-www-form-urlencoded" });
            return _ajax.request(formData, callback);
        }

    },
        /**
         *
         * @public
         * */
        this.sleep = function sleep(msec) {
            var start = new Date().getTime();
            while (true) if (new Date().getTime() - start > msec) break;
        };
    /**
     * 解码
     * @param {*} str 
     * @returns 
     */
    this.decodeURI = this.unescape = function unescape(str) {
        if (this.isString(str)) {
            return window.decodeURIComponent(str);
        }
        return str
    };
    /**
     * @description escape() 函数可对字符串进行编码，这样就可以在所有的计算机上读取该字符串。
     * @description 该方法不会对 ASCII 字母和数字进行编码，也不会对下面这些 ASCII 标点符号进行编码： * @ - _ + . / 。其他所有的字符都会被转义序列替换。
     * @description 提示： 使用 unescape() 方法对字符串进行解码。
     * @param {*} str 
     */
    this.encodeURI = this.escape = function escape(str) {
        if (this.isString(str)) {
            return window.encodeURIComponent(str);
        }
        return str

    }
    /**
     * @public
     * */
    this.toJson = function toJson(str) {
        //console.log(str);
        if (str == null || this.getType(str) != '[object String]') {
            return str;
        }
        if (!str || str.length == 0) {
            return null;
        }
        var _data = null;
        try {
            _data = JSON.parse(str);

        } catch (ex) {

            try {
                _data = window.eval('(' + str + ')');

            } catch (ex1) {
                try {
                    _data = window["eval"].call(window, str);

                } catch (ex2) {

                    _data = { msg: 'JSON格式化失败', data: str, success: false };

                    this.showHtml("JSON格式化失败", str);

                    console.error(ex);
                }
            }

        }

        return _data;
    };

    /**
     * @public
     * */
    this.toString = function toString(obj) {

        if (obj == undefined) {
            return "null";
        }
        var r = [];
        if (typeof obj == "string") {
            return "\"" + obj.replace(/([\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "\\t") + "\"";
        }
        if (typeof obj == "object") {
            if (!obj.sort) {

                for (var i in obj) {
                    if (typeof (obj[i]) == 'function') {
                        continue;
                    }
                    r.push("\"" + i + "\":" + this.toString(obj[i]));
                }

                r = "{" + r.join() + "}"
            } else {
                for (var i = 0; i < obj.length; i++)
                    r.push(this.toString(obj[i]))
                r = "[" + r.join() + "]";
            }
            return r;
        }

        return obj.toString().replace(/\"\:/g, '":""');
    };
     
    /**
     * @description 将字符串转化为dom节点
     * @public
     * */
    this.toHtml = this.parseDom = function parseDom(str) {
        var objE = document.createElement('div');
        objE.innerHTML = str;
        return objE.childNodes;
    };
    /**
     * @description  redjs控件输出未json
     * @returns 
     */
    this.controlToJson = function controlToJson(ctrl,propertyNames)
    { 

        var common = [
            'xtype','modular', 'title','label','header', 'height','width','flex','style','cls','hidden', 'layout',
            'dataSource','text','html','src','href',
            'data','top','banner','content','services','bottom'
        ];
        var _keys={
            'label':['position','space','visible'],
            'header':['position','space','visible'],   
            'layout':['type','align'],
            'modular':['value','style','animate'],
            'collection':['layout','itemXtype','itemStyle','columnCount'],
            'style':['height','width','flex','display','margin','left','right','top','bottom',
                    'textAlign','color','fontSize','fontWeight','lineHeight', 
                    'backgroundColor','backgroundImage'
            ]
             
        };
        let str = '';
 
        for (let key in ctrl) {

            if(this.isEmpty(ctrl[key]))
            {
                //console.log(key,ctrl[key]);
                continue;
            }
            if(this.isFunction(ctrl[key]))
            {
                continue;
            }
             
            var _k = key.replaceAll('_','');
 
            if((propertyNames && propertyNames.indexOf(_k)>=0) ||  common.indexOf(_k)>=0)
            {
                
            }
            else
            {
                //console.log('ignore member:',key,_k)
                continue;
            }
             
             if(this.isString(ctrl[key]))
             {
                 str += `"${_k}":"${ctrl[key]}",`;
                continue;
             }

            if (this.isObject(ctrl[key]))
            {
                //if (ctrl.hasOwnProperty(key)) 
                {
                    str += `"${_k}":${this.controlToJson(ctrl[key],_keys[_k])},`;
                }
                continue;
            }

            if(this.isArray(ctrl[key]))
            {
                 str += `"${_k}":[`;
                for(var i in ctrl[key])
                {
                    str += `${this.controlToJson(ctrl[key][i])},`;
                }
                  str += `{}],`;//多加了一个
                 continue;
            }
            
            {
                str += `"${_k}":"${ctrl[key]}",`;
            }

            //console.log(key, ctrl[key]); 
            
        }
        return `{${str.slice(0, -1)}}`; 
    };
    /**
     * @public 
     * @description 获取对象的jsonSchema
     * https://json-schema.org/understanding-json-schema/
     * @param {*} json 
     * @param {*} filterArr 要解析或不解析的属性名数组，空表示全部
     * @param {*} include  对filterArr作用控制，true:只解析filterArr指定的属性，false:不解析filterArr指定的属性
     */
    this.getJsonSchema = function getJsonSchema(data,filterArr, include) {
        var schema = {};
        var _data = data || {};

        var _fn_full_common_pro = function (examples) {

            this.$ref = "";//通过设置id,引用其他类型的
            this.dependencies = null;//属性依赖关系
            this.minProperties = 0;//属性个数限制
            this.maxProperties = 0,//属性个数限制
            this.additionalProperties = false;//是否允许额外属性
            this.enum = null;
            this.default = null;
            this.examples = examples;
            this.allOf = [];//[schema1,schema2]满足allOf数组中的所有Json Schema。
            this.anyOf = [];//[schema1,schema2]满足anyOf数组中的任意个Schema。
            this.oneOf = [];//[schema1,schema2]满足且进满足oneOf数组中的一个Schema，这也是与anyOf的区别。
            this.not = [];//[schema1,schema2]不严格规定Json数据应满足什么要求，它告诉Json不能满足not所对应的Schema。

        };

        for (var p in _data) {

            if (this.isFunction(_data[p])) {
                continue;
            }
            if (_data[p] instanceof Rsd.common.ComponentX) {
                console.log('is redjs control',_data[p]);    
                continue;
            }
 
            if (filterArr && filterArr.length > 0) {
                var _is_macth = false;
                for(var i = 0;i<filterArr.length;i++) {

                    var regex = filterArr[i];
                    if(regex.test)
                    {
                        _is_macth = _is_macth || regex.test(p);
                    }
                    else
                    {
                        _is_macth = _is_macth || regex == p;
                    }
                    
                    //console.log(p,regex,_is_macth);
                    if(_is_macth)
                    {
                        break;
                    }
                }
 
                if (_is_macth) {
                    if (include === false) {
                        continue;
                    } 
                }
                else {
                    if (include === true) {
                        continue
                    }
                }
            }

            schema[p] = {
                $id: p,
                title: '',
                description: "",
            };
            if (schema[p] == null) {
                schema[p].type = 'null';
                _fn_full_common_pro.call(schema[p], [_data[p]]);
                continue;
            }

            if (Rsd.isString(_data[p])) {
                schema[p].type = 'string';
                schema[p].minLength = 0;
                schema[p].maxLength = 0;
                schema[p].pattern = "";
                schema[p].format = "";
                _fn_full_common_pro.call(schema[p], [_data[p]]);
                continue;
            }
            if (Rsd.isDate(_data[p])) {
                schema[p].type = 'date';
                schema[p].format = "";
                _fn_full_common_pro.call(schema[p], [_data[p]]);
                continue;
            }
            if (Rsd.isBoolean(_data[p])) {
                schema[p].type = 'boolean';
                _fn_full_common_pro.call(schema[p], [_data[p]]);
                continue;
            }
            if (Rsd.isNumber(_data[p])) {
                schema[p].type = 'number';
                schema[p].multipleOf = 0;//约束目标数值必须是该值的整数倍
                schema[p].minimum = null;
                schema[p].maximum = null;
                schema[p].exclusiveMinimum = null;
                schema[p].exclusiveMaximum = null;
                _fn_full_common_pro.call(schema[p], [_data[p]]);
                continue;
            }
            if (Rsd.isArray(_data[p])) {
                schema[p].type = 'array';
                schema[p].minItems = 0;
                schema[p].maxItems = 0;
                var cols = _data[p].length > 0 ? this.getJsonSchema(_data[p][0],filterArr, include) : {};
                schema[p].items = { type: cols.length > 0 ? 'object' : 'string', properties: cols };//统一的数组元素的Schema 复制对象，不同元素约束用数组
                schema[p].additionalItems = [];//其他数组元素约束
                _fn_full_common_pro.call(schema[p], _data[p]);
                continue;
            }
            if (Rsd.isObject(_data[p])) {
                //console.log('is object data',p,_data[p]);   
                //continue; 
                schema[p].type = 'object';
                schema[p].properties = this.getJsonSchema(_data[p],filterArr, include);
                schema[p].required = [];//必填属性名称数组["name","age"]
                _fn_full_common_pro.call(schema[p], []);
                continue;
            }

        }

        return schema;
    }
    /**
     * @public
     * */
    this.getType = function getType(obj) {
        return Object.prototype.toString.apply(obj);
    };

    /**
     *@public
     * */
    this.isEmpty = function isEmpty(obj) {


        if (obj === undefined || obj === null || obj === '') {
            return true;
        }
        if (obj instanceof Function) {
            return false;
        }
        if (obj instanceof Array) {
            return obj.length == 0
        }

        if (obj instanceof Date) {
            return false
        }
        if (Rsd && Rsd.common && Rsd.common.Object && obj instanceof Rsd.common.Object) {
            return false
        }
        if (obj instanceof HTMLElement) {
            return false;
        }
        if (obj instanceof Node) {
            return false;
        }
        if (obj instanceof Object) {

            return Object.keys ? Object.keys(obj).length == 0 : JSON.stringify(obj) == "{}";
        }

        return false;
    };

    /**
     * @public
     * */
    this.isNull = function isNull(obj) {
        return this.getType(obj) == '[object Null]';

    };

    /**
     *@public
     *
     * */
    this.isNaN = function isNaN(obj) {
        if (window) {
            return window.isNaN(obj);
        }
        return Number.isNaN(obj);
    };

    /**
     * */
    this.isUndefined = function isUndefined(obj) {
        return this.getType(obj) == '[object Undefined]';
    };

    /**
     * @public
     * */
    this.isNullOrUndefined = function isNullOrUndefined(obj) {
        return this.isNull(obj) || this.isUndefined(obj);
    };

    /**
     * @public
     * */
    this.isBoolean = function isBoolean(obj) {
        return this.getType(obj) === this.getType(true);
    };

    /**
     * @public 是否为true
     * */
    this.isTrue = function isTrue(obj) {

        if (this.isNullOrUndefined(obj)) {
            return false;
        }

        if (this.isBoolean(obj)) {
            return obj == true;
        }

        if (this.isString(obj)) {
            return obj.toLowerCase() == 'true';
        }

        if (this.isNumber(obj)) {
            new Boolean(obj) == true;
        }

        return new Boolean(obj) == true;
    };

    /**
     * @public
     * */
    this.isString = function isString(obj) {
        return this.getType(obj) === this.getType('');
    };

    /**
     * @public
     * @description 是否是数字
     * */
    this.isNumber = function isNumber(obj) {

        return this.getType(obj) === this.getType(0) && !isNaN(Number(obj));
    };

    /**
     * @public
     * @description 是否是数字
     * */
    this.isDate = function isDate(obj) {

        return this.getType(obj) === this.getType(new Date());
    };
    /**
     * @public
     * */
    this.isFunction = function isFunction(obj) {
        return this.getType(obj) == this.getType(function () { });
    };
    /*
    *
    * */
    this.isArguments = function isArguments(obj) {
        return this.getType(obj) == this.getType(arguments);
    };

    /**
     * @public
     * @description 是否是原生态对象,Rsd.getType(obj) == this.getType({})
     * */
    this.isObject = function isObject(obj) {
        return this.getType(obj) == this.getType({});
    };

    /**
     * @public
     * @description  判断对象是否是数组
     * @return boolean
     * */
    this.isArray = function isArray(obj) {

        return this.getType(obj) == this.getType([]);
    };

    /**
     * @public
     * @description js 的类型也是有作用域限制的，不同window下的即时相同类型名，也不是 同一个类型,obj instanceof type 或 类型名称相同
     * @param {object} obj 对象
     * @param {class} type 类型名称
     * @return boolean
     * */
    this.isType = function isType(obj, type) {
        if(obj==null)
        {
             return false;
        }

        if (this.isString(type)) {
            return this.getType(obj) == type;
        }
        //特别提醒：js 的类型也是有作用域限制的，不同window下的即时相同类型名，也不是 同一个类型

        //仅支持相同window域，支持继承判断
        if(obj instanceof type)
        {
            return true;
        }
        //支持不同window域，但不支持继承判断
        if(this.getType(obj) == '[object ' + type.name + ']')
        {
            return true;
        }
        //支持不同window域，且支持继承判断
        var _is_continue = true;
        var _p = Object.getPrototypeOf(obj);;
        while(_is_continue)
        { 
            if(_p==null)
            {
                break;
            }
            _is_continue = this.getType(_p) != '[object ' + type.name + ']'
            
            if(_is_continue)
            {
                _p = Object.getPrototypeOf(_p); 
            } 
            //console.log(this.getType(_p),type.name,!_is_continue);
        }
        return !_is_continue;
    };

    /**
     * @public
     * @description获取提示语言
     * */
    this.lang = function lang(name) {

        var lang = Rsd.resources.lang || {};
        var lang = lang[Rsd.app.lang];

        var arr = name.split('.');
        for (var i in arr) {
            if (lang == null) {
                break;
            }
            lang = lang[arr[i]];
        }
        return lang;
    };

    /**
     * @public
     * @description getUserAgent*/
    this.getUserAgent = function getUserAgent() {
        return navigator.userAgent.toLowerCase();
    };

    /***
     * 获取浏览器指纹数据
     */
    this.getBrowserFingerprint = function getBrowserFingerprint() {
        const fingerprint = {
            userAgent: navigator.userAgent,
            screenResolution: `${screen.width}x${screen.height}`,
            colorDepth: screen.colorDepth,
            timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
            plugins: Array.from(navigator.plugins).map(plugin => plugin.name).join(', '),
            language: navigator.language,
            platform: navigator.platform,
        };

        return fingerprint;

    }
    /**
     * 将浏览器指纹数据转化为base64的 浏览器指纹ID
     */
    this.getBrowserId = function getBrowserId() {
        var fingerprint = this.getBrowserFingerprint();
        var str = fingerprint.userAgent + "|"
            + fingerprint.screenResolution + "|"
            + fingerprint.colorDepth + "|"
            + fingerprint.timezone + "|"
            + fingerprint.plugins + "|"
            + fingerprint.language + "|"
            + fingerprint.platform + "|"
            ;
        return Rsd.encodeBase64(str);
    };
    /**
     * 将浏览器指纹ID添加到cookie
     */
    this.addBrowserIdToCookie = function addBrowserIdToCookie() {
        this.addCookie('rsd.browser.id', this.getBrowserId());
    }

    /**
     * 
     * @param {*} input 
     * @returns 
     */
    this.compressString = function compressString(input) {
        let compressed = '';
        let count = 1;

        for (let i = 0; i < input.length; i++) {
            if (input[i] === input[i + 1]) {
                count++;
            } else {
                compressed += input[i] + count;
                count = 1;
            }
        }

        return compressed.length < input.length ? compressed : input;
    }

    /**
     * 
     * @param {*} input 
     * @returns 
     */
    this.decompressString = function decompressString(input) {
        let decompressed = '';

        for (let i = 0; i < input.length; i++) {
            let char = input[i];
            let count = '';

            while (!isNaN(input[i + 1])) {
                count += input[i + 1];
                i++;
            }

            decompressed += char.repeat(parseInt(count, 10));
        }

        return decompressed;
    }
    /**
     * @public
     *@description 判断是否是移动端
     * */
    this.isMobile = function isMobile() {
        if(typeof navigator === 'undefined')
        {
            return false;
        }
        return /phone|pad|pod|iPhone|iPod|ios|iPad|android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone|Opera Mini/i.test(navigator.userAgent);
    }

    /**
     *
     * @public
     * @description 判断是否是Chrome浏览器
     * */
    this.isChrome = function isChrome() {
        return /\bchrome\b/.test(this.getUserAgent());
    };
    /**
     * @public
     * @description 判断是否是微信浏览器
     * */
    this.isWeChat = function isWeChat() {

        if (navigator && navigator.userAgent) {
            var ua = navigator.userAgent.toLowerCase();
            if (ua.match(/MicroMessenger/i) == "micromessenger") {
                return true;
            } else {
                return false;
            }
        }
        return false;
    };

    /**
     * @public
     * @description Gets the classname of an object or function if it can.
     *  @description Otherwise returns the provided default.
     *
     */
    this.getFunctionName = function getFunctionName(fn, defaultName) {


        var nameFromToStringRegex = /^function \s?([^\s(]*)/;
        var result = "";
        if (typeof fn === 'function') {
            try {
                result = fn.name || fn.toString().match(nameFromToStringRegex)[1];
            }
            catch (ex) {
                console.error("Get Function Name Fail", fn);
            }

        } else if (typeof fn.constructor === 'function') {
            result = getFunctionName(fn.constructor, defaultName);
        }
        return result || defaultName;
    };

    /**
     * @public
     * @description 打印方法调用栈信息
     * */
    this.printCallStack = function printCallStack(text, count) {

        var _count = 0;
        console.log('===========' + (text || '') + '=============');
        var caller = arguments.callee.caller; //当前方法的调用者
        while (caller && (_count < count || !count)) {
            console.log(caller);
            caller = caller.caller;
            _count++;
        }
    };

    /**
     * @public
     * */
    this.print = function print(id) {

        if (arguments.length == 0) {
            window.print();
            return;
        }
        var _list = [];
        var _node;
        for (var i in document.body.childNodes) {
            _node = document.body.childNodes[i];
            if (_node instanceof Element) {
                _list.push({ node: _node, display: _node.style.display });
                _node.style.display = 'none';
            }

        }
        // A4 210mm* 297mm 1in=25.4mm
        var _dpi = this.getDPI();
        var _a4_width = _dpi.width * 210 / 25.4;
        document.body.style.width = _a4_width + 'px';
        alert(document.body.style.width);
        var _div = document.getElementById(id);
        var _temp = document.createElement('div');
        _temp.innerHTML = _div.innerHTML;
        document.body.appendChild(_temp);
        window.print();
        document.body.removeChild(_temp);
        for (var i in _list) {
            _list[i].node.style.display = _list[i].display;
        }
        return;
    };

    /**
     * @public
     * @description 复制数据到剪贴板
     * */
    this.copy = function copy(data) {


        var currentFocus = document.activeElement;

        if (window.clipboardData && window.clipboardData.setData('Text', data)) // 赋予 text 格式的数据
        {
            this.showPopup("复制成功!");
            return;
        }

        var _text_for_copy = document.getElementById('rsd_text_for_copy');
        if (_text_for_copy == null) {
            _text_for_copy = document.createElement('textarea');
            _text_for_copy.setAttribute('id', 'rsd_text_for_copy');
            //_text_for_copy.style.width ='0px';
            // _text_for_copy.style.height ='0px';
            _text_for_copy.style.position = "absolute";
            _text_for_copy.style.left = "-9999px";
            _text_for_copy.style.top = "0";


            document.body.appendChild(_text_for_copy);
        }

        _text_for_copy.textContent = data;
        _text_for_copy.setSelectionRange(0, _text_for_copy.value.length);
        _text_for_copy.focus();
        _text_for_copy.select();

        var _flag = document.execCommand != undefined;
        try {
            //第一始终失败
            _flag = document.execCommand('copy');
            //第二次成功
            _flag = _flag || document.execCommand('copy');

        } catch (err) {
            this.error("复制数据失败", "", err);
        }

        if (_flag) {
            this.showPopup("复制成功！");

        } else {
            var _d = this.create('Rsd.container.Dialog', { width: 600, height: 420, layout: 'vbox', title: '复制数据' });
            var _t = this.create('Rsd.form.TextArea', {
                label: false,
                flex: 1, width: '98%',
                value: data,
                margin: '5 1% 5 1%',
                style: { border: "1px solid grey" }
            });
            _d.add(_t);
            _d.showDialog();
            _t.focus();
            _t.select();
        }
        if (currentFocus && typeof currentFocus.focus === "function") {
            currentFocus.focus();
        }
        document.body.removeChild(_text_for_copy);

        return;

    };


    /**
     * @public
     * @description 显示等待窗口
     * */
    this.showWaiting = function showWaiting(key, parent, msg) {
        //console.error(key,parent);
        var _key = key || (parent && parent.id) || Math.random();
        if (this.isEmpty(_key)) {

            throw new Erorr('key is null. Rsd.showWaiting = function showWaiting(key, parent,msg)');
        }

        var me = Rsd || this;
        setTimeout(function (parent) {
            me.__waitings = me.__waitings || {};
            if (!me.__waitings[_key]) {
                me.__waitings[_key] = me.create('Rsd.control.WaitingBox', {
                    key: _key,
                    message: msg || me.lang('common.waitingText'),
                    border: false
                });
            }
            me.__waitings[_key].showDialog(document.body);
        }, 0);
        //console.debug(new Error('debug info in show waiting'));
        return _key;
    };

    /**
     * @public
     @description 关闭等待窗口
     * */
    this.closeWaiting = function closeWaiting(key, delay) {
        if (this.isEmpty(key)) {
            throw new Erorr('key is null. Rsd.closeWaiting=function closeWaiting(key, delay)');
        }
        var me = Rsd || this;
        setTimeout(function () {
            if (me.__waitings && me.__waitings[key]) {
                me.__waitings[key].close();
                delete me.__waitings[key];
            }
        }, (delay == undefined || delay == null) ? 500 : delay);
    };

    /**
     * @description 使用window.open 打开新的url 
     * @public
     * */
    this.showModalDialog = function showModalDialog(url, title, height, width, data) {

        if (window.showModalDialog) {
            window.showModalDialog(url);
        }
        else {
            var _h = height || 400;
            var _w = width || 500;
            var _params = 'height=' + _h + ', width=' + _w + ', top=' + (((window.screen.height - _h) / 2) - 50) + ',left=' + ((window.screen.width - _w) / 2) + ',toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no';
            window.open(url, title, _params);
        }
    };

    /**
     * @public
     * */
    this.get = function get(id) {
        return this.objects[id];
    };

    /**
     * @public
     * */
    this.getDom = function getDom(id) {

        return document.getElementById(id);
    };

    /**
     * @description 获取文件扩展名
     * @param {*} url 
     * @returns 
     */
    this.getFileExt = function getFileExt(url) {
        var file = url;

        var FileExt = file.replace(/.+\./, "");   //正则表达式获取后缀
        return FileExt;
    };

    /**
     * @description 获取文件名
     * @param {*} url 
     * @param {*} ext 是否包含扩展名
     * @returns 
     */
    this.getFileName = function getFileName(url, ext) {
        var file = url;
        var pos = file.lastIndexOf('/');
        file = file.substring(pos + 1);
        if (ext) {
            return file;
        }
        pos = file.lastIndexOf('.');
        return file.substring(0, pos);
    };

    /**
     * @public
     * @desc 映射成当前网站网站目录
     * */
    this.mappingUrl = function mappingUrl(url) {
        if (this.isNullOrUndefined(this.__homePath)) {
            this.__homePath = window.location.protocol + '//' + window.location.host + '/' + window.location.pathname + '/';
        }
        return this.__homePath + url;
    };
    /*
    * RedjsHost是否是代理服务器
    * */
    this.isAgentHost = function isAgentHost() {
        return this.__isAgentHost || false;
    };
    /**
     *@public
     *@description 获取redjs框架服务器地址，支持将框架部署在远程服务器上
     * */
    this.getRedjsHost = function getRedjsHost() {

        //debugger;
        if (this.isEmpty(this.__jsHomeHost)) {

            var result = ""

            var e = Error('redjs');
            if (e.fileName) {//firefox
                result = e.fileName;
            } else if (e.sourceURL) {//safari
                result = e.sourceURL;
            } else if (e.stacktrace) {//opera
                var m = e.stacktrace.match(/\(\) in\s+(.*?\:\/\/\S+)/m);
                if (m && m[1])
                    result = m[1];
            }
            if (!result) {//IE与chrome
                var scripts = document.getElementsByTagName("script");
                var reg = /rsd([.-]\d)*\.js(\W|$)/i
                for (var i = 0, n = scripts.length; i < n; i++) {
                    var src = !!document.querySelector ? scripts[i].src : scripts[i].getAttribute("src", 4);
                    if (src && reg.test(src)) {
                        result = src
                        break;
                    }
                }
            }
            var _path = result.substr(0, result.lastIndexOf('/') + 1);

            var _d = _path.split('/');
            this.__jsHomeHost = _path.replace(_d[_d.length - 1], '');
        }

        if (window.location.protocol == 'https:' && this.__jsHomeHost.startsWith('http://')) {
            this.__jsHomeHost = 'https://' + this.__jsHomeHost.substring(7);
        }
        if (window.location.protocol == 'http:' && this.__jsHomeHost.startsWith('https://')) {
            //this.__jsHomeHost = 'http://' + this.__jsHomeHost.substring(8);
        }

        return this.__jsHomeHost;
    };
    /** 
     * 获取完整的redjs框架地址
     * isAgentHost：true 表示jsHomePath指向的服务期是否代理服务器，代理服务器的求情格式为jsHomePath/base64(path),需要在代理服务上 先解析path内容 
     * isAgentHost：false 表示正常结构
    */
    this.getRedjsUrl = function getRedjs(url) {
        var _url = url||'';

        if (_url.startWith('/')) {
            _url = _url.substr(1);
        }

        if (this.isAgentHost()) {
            return this.getRedjsHost() + this.utf8ToBase64(_url);
        }
        else {
            return this.getRedjsHost() + _url;
        }
    }
    /**
     *@public
     *@description重定向
     * */
    this.redirect = function redirect(url) {
        window.location.href = url;
    };

    /**
     * @public
     * @description 获取url参数
     * */
    this.getUrlParam = function getUrlParam(url, name) {

        if(typeof URL==='undefined')//URL类 仅在浏览器环境存在
        {
            return null;
        }
        var _url = url || window.location.href;
        var _name = name;
        if (arguments.length == 1) {
            _url = window.location.href;
            _name = arguments[0];
        }
      
        //console.log(arguments,_url,_name);
        var urlObj = new URL(_url);
        var urlParams = urlObj.searchParams; 
        var query = Object.fromEntries(urlParams.entries());
        return query[_name];
    };

    /**
     *
     *@public
     *@description 设置url参数
     * */
    this.setUrlParam = function setUrlParam(url, name, value) {

        var _url = url || (window.location.protocol + '//' + window.location.host + '/' + window.location.pathname + '/');

        var _params = {};

        if (arguments.length == 2 && this.isObject(arguments[1])) {
            _params = arguments[1];
        }
        if (arguments.length == 3) {

            if (this.getUrlParam(name) == value) {
                return url;
            }
            else {
                _params[name] = value;
            }
        }

        var currentUrl = _url;
        //console.log(_url);
        var index = _url.indexOf('#');
        if (index > 0) {
            currentUrl = _url.substr(0, index);
        }

        for (var i in _params) {
            var _name = i;
            var _value = _params[i];

            if (/\?/g.test(currentUrl)) {
                var exp = new RegExp(_name + '[%20]{0,100}=[%20]{0,100}[-\\w]{0,100}');
                if (exp.test(currentUrl)) {
                    currentUrl = currentUrl.replace(exp, _name + "=" + _value);
                } else {
                    currentUrl += "&" + _name + "=" + _value;
                }
            } else {
                currentUrl += "?" + _name + "=" + _value;
            }
        }
        if (index > 0) {
            currentUrl = currentUrl + '#' + _url.substr(index, index);
        }

        return currentUrl;
    };

    /**
     *@public
     * */
    this.getByteLength = function getByteLength(val) {
        var len = 0;
        for (var i = 0; i < val.length; i++) {
            if (val[i].match(/[^x00-xff]/ig) != null) //全角
                len += 2;
            else
                len += 1;
        }
        ;
        return len;
    };

    /**
     * @public
     * @description 调用方法执行
     * @param {Rsd.common.Object} caller,
     * @param {function|string} fn,
     * @param {array} args []
     * */
    this.callFunction = function callFunction(caller, fn, args) {
        var _caller = null;
        var _fn = null;
        var _args = null;

        for (var i = 0; i < arguments.length; i++) {
            if (this.isFunction(arguments[i]) || this.isString(arguments[i])) {
                _fn = arguments[i];
                continue;
            }
            if (Rsd.isObject(arguments[i]) ||
                (Rsd.common && Rsd.common.Object && arguments[i] instanceof Rsd.common.Object)) {
                _caller = arguments[i];
                continue;
            }
            if (this.isArray(arguments[i])) {
                _args = arguments[i];
                continue;
            }
            if (_args == null && this.isArguments(arguments[i])) {
                var _args = Array.prototype.slice.call(arguments[i]);
                continue;
            }
        }

        if (this.isFunction(_fn)) {
 
            return _fn.apply(_caller || window.Rsd, _args || []);
        }
        if (this.isString(_fn)) {
            if (!(_caller instanceof Rsd.common.Object)) {
                this.error('argument caller is not instanceof Rsd.common.Object,when call function callFunction(caller,fn,args).');
                return true;
            }
            return _caller.funApplyByIOC(_fn, _args || []);
        }
        return null;
    };

    /**
     @public
     @description 获取字符宽度，像素值
     @param val：文本,
     @param size：单个英文字符文本宽度（像素）
     @return 文本宽度
     @type Number
     * */
    this.getStringWidth = function getStringWidth(container, str) {
        var span = document.createElement("span");
        var _parent = container || document.body;
        _parent.appendChild(span);
        span.style.visibility = "hidden";
        span.style.whiteSpace = "nowrap";
        span.innerText = str;
        var _width = span.offsetWidth;
        _parent.removeChild(span);
        return _width;
    };

    /**
     * @public
     *  @description 货币格式化，不做四舍五入处理
     *  @param {number} num 金额
     *  @param {string} currency 货币符号 ￡(英镑) ￥(人民币,日元) €(欧元) ＄(美元)
     *  @return string
     * */
    this.formatCurrency = function formatCurrency(num, currency) {
        if (num == undefined || num == null) {
            return "";
        }
        num = num.toFixed(2);
        //num = this.toString(num);
        var str = (currency || '') + num.split('').reverse().join('').replace(/(\d{3}(?=\d)(?!\d+\.|$))/g, '$1,').split('').reverse().join('');
        return str;
    };

    /**
     * @public
     * @description 金额小写转大写
     * */
    this.formatCurrencyCN = function formatCurrencyCN(num) {
        var fraction = ['角', '分'];
        var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
        var unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']];
        var head = num < 0 ? '欠' : '';
        num = Math.abs(num);

        var s = '';

        for (var i = 0; i < fraction.length; i++) {
            s += (digit[Math.floor(num * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
        }
        s = s || '整';
        num = Math.floor(num);

        for (var i = 0; i < unit[0].length && num > 0; i++) {
            var p = '';
            for (var j = 0; j < unit[1].length && num > 0; j++) {
                p = digit[num % 10] + unit[1][j] + p;
                num = Math.floor(num / 10);
            }
            s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
        }
        return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整');

    };

    /**
     * @public
     * @description 时间转化
     * @param {Number|String} timestamp
     * @param {string} [format] 格式：yyyy-MM-dd|yyyy-MM-dd hh:mm:ss|yyyy-MM-dd hh:mm:ss S
     * */
    this.formatTimestamp = function formatTimestamp(timestamp, format) {
        var _s = timestamp;
        if (this.isString(_s)) {
            _s = Number(_s);
        }
        if (_s > 0) {
            while (_s.toString().length < 13) {
                _s = _s * 10;
            }
        }

        if (this.isNumber(_s)) {
            return new Date(_s).format(format || 'yyyy-MM-dd hh:mm:ss');
        }
        return timestamp;
    };

    /**
     * @public
     * @description 数字格式化
     * @param {Number|String} value
     * @param {Number} precision
     * */
    this.formatNumber = function formatNumber(value, precision) {
        var _n = Number(value);
        if (isNaN(_n)) {
            return 0;
        }
        return _n.toFixed(precision || 0);
    };

    /**
     * @public
     * @description 格式化json
     * @param {string|object} json
     * @param {Function|String|Number} [replacer]
     * @param {Number|String} [space]
     * */
    this.formatJson = function formatJson(json, replacer, space) {
        return JSON.stringify(json, replacer || null, space || 4);
    };

    /**
     * @public
     * @description 检测浏览器及其版本
     * */
    this.detectNavigator = function detectNavigator() {

        var ua = navigator.userAgent.toLowerCase();
        var s;
        (s = ua.match(/msie ([\d.]+)/)) ? this.ie = s[1] :
            (s = ua.match(/firefox\/([\d.]+)/)) ? this.firefox = s[1] :
                (s = ua.match(/chrome\/([\d.]+)/)) ? this.chrome = s[1] :
                    (s = ua.match(/opera.([\d.]+)/)) ? this.opera = s[1] :
                        (s = ua.match(/version\/([\d.]+).*safari/)) ? this.safari = s[1] : 0;

        //以下进行测试
        if (this.ie) return ('IE: ' + this.ie);
        if (this.firefox) return ('Firefox: ' + this.firefox);
        if (this.chrome) return ('Chrome: ' + this.chrome);
        if (this.opera) return ('Opera: ' + this.opera);
        if (this.safari) return ('Safari: ' + this.safari);
    };

    /**
     * @public
     @description 检查操作系统
     * */
    this.detectOS = function detectOS() {
        var sUserAgent = navigator.userAgent;
        var isWin = (navigator.platform == "Win32") || (navigator.platform == "Windows");
        var isMac = (navigator.platform == "Mac68K") || (navigator.platform == "MacPPC") || (navigator.platform == "Macintosh") || (navigator.platform == "MacIntel");
        if (isMac) return "Mac";
        var isUnix = (navigator.platform == "X11") && !isWin && !isMac;
        if (isUnix) return "Unix";
        var isLinux = (String(navigator.platform).indexOf("Linux") > -1);
        if (isLinux) return "Linux";
        if (isWin) {
            var isWin2K = sUserAgent.indexOf("Windows NT 5.0") > -1 || sUserAgent.indexOf("Windows 2000") > -1;
            if (isWin2K) return "Win2000";
            var isWinXP = sUserAgent.indexOf("Windows NT 5.1") > -1 || sUserAgent.indexOf("Windows XP") > -1;
            if (isWinXP) return "WinXP";
            var isWin2003 = sUserAgent.indexOf("Windows NT 5.2") > -1 || sUserAgent.indexOf("Windows 2003") > -1;
            if (isWin2003) return "Win2003";
            var isWinVista = sUserAgent.indexOf("Windows NT 6.0") > -1 || sUserAgent.indexOf("Windows Vista") > -1;
            if (isWinVista) return "WinVista";
            var isWin7 = sUserAgent.indexOf("Windows NT 6.1") > -1 || sUserAgent.indexOf("Windows 7") > -1;
            if (isWin7) return "Win7";
        }
        return "other";

    };
    /**
    *  @public
    * @description 创建mailTo dom对象
    * */
    this.mailTo = function mailTo() {
        console.error('暂未实现。')
        return [];
    };

    /**
     * @public
     * @description md5加密
     * */
    this.md5 = function md5(str) {

        if ($ && $.md5) {
            return $.md5(str);
        }
        else {
            this.error('$.md5(jquery md5插件) 加密插件不存在。')
        }
        return null;
    };

    /**
     * @public
     * @description 是否包含属性 name ，含其原型的属性。
     * */
    this.hasProperty = function hasProperty(obj, name) {
        if (obj == null || obj == undefined) {
            return false;
        }
        if (obj instanceof Object) {
            return name in obj;
        }
        if (obj.hasOwnProperty) {
            return obj.hasOwnProperty(name);
        }
        return false;
    };

    /**
     *@public
     * @description 数组拼接
     * arr1,arr2.... arr n
     * */
    this.arrayConcat = function arrayConcat(arr1, arr2) {
        var _arr = [];
         
        for (var i in arguments) {
             
            if (this.isArray(arguments[i]) && arguments[i].length > 0) {
                //console.log(arguments[i]);    
                Array.prototype.push.apply(_arr, arguments[i]);
            } 
        } 
        //console.log(_arr);
        return _arr;
    };

    /**
     * * @description 定义枚举类型
     * url:'http://hermes.redmicro.cn/dev/getenum?enumtype=' + enumName
     */
    this.defineEnum = function defineEnum(url, enumName, valueMember, method, authCode) {
        if (!Rsd.classes[enumName]) {

            Rsd.httpRequest({
                url: url,
                method: method || 'GET',
                authCode: authCode || '',
                async: false
            }, function (data) {

                if (!Rsd.isEmpty(data.data)) {
                    Rsd.define(data.data.name, {
                        extend: 'Rsd.data.Enum',
                        "valueMember": valueMember || "Value",
                        "codeMember": "Code",
                        "textMember": "Text",
                        "colorMember": "Color",
                        items: data.data.items,
                        constructor: function constructor() {

                        }
                    });

                    _enum = Rsd.widget(enumName, {});

                } else {
                    Rsd.warn('类型：' + enumName + '不存在。');
                }


            });

        }
    },
        
    /**
    * 
    * @param {*} url 枚举数据获取地址
    * url:'http://hermes.redmicro.cn/dev/getenumlist'
    */
    this.defineEnums = function defineEnums(url, method, authCode) {

            this.httpRequest({
                url: url,
                method: method || 'GET',
                authCode: authCode || ''
            }, function (data) {


                if (data.success) {
                    var list = data.data;
                    for (var i in list) {
                        var _enum = list[i];
                        if (_enum == null) {
                            continue;
                        }
                        var _typeName = _enum.typeName || _enum.TypeName
                        if (Rsd.isEmpty(_typeName)) {
                            console.error('枚举信息错误：', _enum);
                            continue;
                        }
                        if (Rsd.classes && Rsd.classes[_typeName]) {
                            continue;
                        }

                        try {
                            Rsd.define(_typeName, {
                                extend: 'Rsd.data.Enum',
                                "valueMember": "Value",
                                "codeMember": "Code",
                                "textMember": "Text",
                                items: _enum.Items,
                                constructor: function constructor() {

                                }
                            });
                        }
                        catch (err) {
                            console.error(err);
                        }

                    }

                }
                else {
                    console.error('(' + data.code + ')' + data.msg + ", From Request URL: " + url);
                }
            });
    };

    //以下方法 快捷生成dom组件
    /**
     * 
     * @param {*} url 
     * @param {*} style 
     * @param {*} tip 
     * @returns 
     */
    this.image = function image(url, style, tip) {
        var img = document.createElement('img');
        img.setAttribute('src', url);
        if (this.isObject(style)) {

            this.setElStyle(img, style);
        }
        if (Rsd.isString(tip)) {
            img.setAttribute('title', tip);
        }

        return img;

    };
    /**
     * 
     * @param {*} checked 
     * @param {*} readonly 
     * @returns 
     */
    this.checkbox = function checkbox(checked, readonly) {
        var chk = document.createElement('input');
        chk.setAttribute('type', 'checkbox');
        chk.setAttribute('readonly', readonly);
        chk.setAttribute('disabled', readonly);
        chk.checked = checked;
        return chk;
    };
    /** 
     * @description 生成标签 
     * @param {object} style 文本颜色或样式对象{color:'red'}
     * @param {string} tip tip 
     * */
    this.label = function label(text, style, tip, deleteFn) {

        var _lbl = document.createElement('label');
        _lbl.classList.add('x-text-label');
        _lbl.innerHTML = text;
        if (deleteFn) {
            _lbl.onmouseenter = function () {

                var id = "del_" + Date.now();
                var _del = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
                _del.setAttribute("id", id);
                _del.style.color = "red";
                //_del.style.backgroundColor="#ffffff";
                _del.style.position = "absolute";
                _del.style.width = "14px";
                _del.style.height = "14px";

                _del.style.transform = "translate(-7px,-4px)";
                _del.setAttribute("viewBox", "0 0 1024 1024");

                var _path = document.createElementNS("http://www.w3.org/2000/svg", "path");
                var _d = "M511.232 438.8352L112.9984 40.6016A51.2 51.2 0 0 0 40.6016 112.9984L438.784 511.232 40.6016 909.4656a51.2 51.2 0 1 0 72.3968 72.448l398.2336-398.2848 398.2336 398.2848a51.2 51.2 0 1 0 72.448-72.448l-398.2848-398.2336 398.2848-398.2336A51.2 51.2 0 0 0 909.4656 40.6016L511.232 438.784z";
                _path.setAttributeNS(null, "d", _d);
                _path.setAttributeNS(null, "fill", 'red');
                _del.appendChild(_path);

                var _title = document.createElementNS("http://www.w3.org/2000/svg", 'title');
                _title.textContent = "点击删除该标签";
                _del.appendChild(_title);

                _del.onclick = deleteFn;

                this.appendChild(_del);
                this.setAttribute("for", id);

            }

            _lbl.onmouseleave = function () {
                var id = this.getAttribute("for");
                var _del = document.getElementById(id);
                if (_del) {
                    _del.parentNode.removeChild(_del);
                }
            }
        }
        if (Rsd.isString(tip)) {
            _lbl.title = tip;
        }
        if (this.isObject(style)) {
            this.setElStyle(_lbl, style);
        }
        if (this.isString(style)) {
            _lbl.style.backgroundColor = style;
            _lbl.style.borderColor = style;
        }
        return _lbl;
    };
    /**
     * @public
     * @description 生成text HtmlElement
     * @param {string} text 文本信息 
     * @param {string} style 样式对象{color:'red'}
     * @param {string} tip tip 
     * */
    this.text = function text(text, style, tip) {
        if (Rsd.isNumber(arguments[1])) {
            console.error('最大宽度应使用样式属性{maxWidth:' + arguments[1] + 'px}控制')
            style = null;
        }
        if (Rsd.isString(arguments[1])) {
            console.error('颜色应使用样式属性{color:' + arguments[1] + '}控制');
            style = null;
        }

        var _t = text || '';
        var _style = style || { overflow: 'hidden', color: 'grey' };
        var _tip = tip || _t;

        var txt = document.createElement('span');
        //console.log(this);
        this.setElStyle(txt, _style);
        txt.appendChild(document.createTextNode(_t))

        if (Rsd.isString(_tip)) {
            txt.title = _tip;
        }
        return txt;
    };

    /**
     * @description 将枚举值，转化为对应的文字输出
     * @param {*} enumName 
     * @param {*} value 
     * @param {*} style 样式对象{color:'red'} 
     * @returns 
     */
    this.enumText = function enumText(enumName, value, style) {
        var _value = value;
        var _txt = Rsd.isEmpty(_value) ? "" : _value;
        var _style = style || {};
        if (!Rsd.xtypes[enumName]) {
            Rsd.warn('类型：' + enumName + '不存在,请先定义该类型。');

            return Rsd.isEmpty(_value) ? "" : _value;
        }
        else {
            _enum = Rsd.widget(enumName, {});
            var _enumItem = _enum.getItem(_value);
            if (_enumItem) {
                _txt = _enumItem.text;
                _style = Rsd.apply({ backgroundColor: _enumItem.color }, _style);
            }
        }

        return Rsd.text(_txt, _style)
    };
    /**
     * @description 将枚举值，转化为对应的文字标签输出
     * @param {*} enumName 
     * @param {*} value 
     * @param {*} style 样式对象{color:'red'}  
     * @returns 
     */
    this.enumLabel = function enumLabel(enumName, value, style) {
        var _value = value;
        var _txt = Rsd.isEmpty(_value) ? "" : _value;
        var _style = style || {};
        if (!Rsd.xtypes[enumName]) {
            Rsd.warn('类型：' + enumName + '不存在,请先定义该类型。');
        }
        else {
            _enum = Rsd.widget(enumName, {});
            var _enumItem = _enum.getItem(_value);
            if (_enumItem) {
                _txt = _enumItem.text;
                _style = Rsd.apply({ backgroundColor: _enumItem.color }, _style);
            }
        }
        return Rsd.label(_txt, _style);
    };
    /**
     * 
     * @param {*} text 
     * @param {*} width 
     * @param {*} color 样式对象{color:'red'}  
     * @param {*} tip 
     */
    this.nobr = function nobr(text, style, tip) {
        if (Rsd.isNumber(arguments[1])) {
            console.error('最大宽度应使用样式属性{maxWidth:' + arguments[1] + 'px}控制')
            style = null;
        }
        if (Rsd.isString(arguments[1])) {
            console.error('颜色应使用样式属性{color:' + arguments[1] + '}控制');
            style = null;
        }
        var _t = text || '';
        var _style = style || { overflow: 'hidden', color: 'grey' };
        var _tip = tip || _t;

        var nobr = document.createElement('span');
        nobr.appendChild(document.createTextNode(_t));

        this.setElStyle(nobr, _style);

        if (Rsd.isString(_tip)) {
            nobr.title = _tip;
        }

        return nobr;
    };

    /**
     * @public
     * @description 创建圆dom对象 function circle(size, color)
     * */
    this.circle = function circle(size, color, text) {

        var _s = size || 10;
        var _c = color || 'green';

        return this.parseDom('<div class="x-icon_circle" style="text-align:center;width: ' + _s + 'px;height: ' + _s + 'px;line-height:' + _s + 'px;color:#ffffff;background-color: ' + _c + ';">' + (text || '') + '</div>')[0];
    };
    /**
     * 
     * @param {*} content 
     * @param {*} style 
     * @returns 
     */
    this.span = function span(content, style) {
        var span = this.parseDom('<span>' + content + '</span>')[0];
        this.setElStyle(span, style || {});
        return span;
    };
    /**
     * @public
     * @description 创建空格dom对象
     * */
    this.blankspan = function blankspan(count, style) {
        var _c = count || 1;
        var _s = '';
        for (var i = 0; i < _c; i++) {
            _s = _s + '&nbsp;';
        }
        var span = this.parseDom('<span>' + _s + '</span>')[0];
        this.setElStyle(span, style || {});
        return span;
    };

    /**
     * @public
     * @description 创建换行dom对象
     * */
    this.newLine = function newLine() {
        return this.parseDom('<br>')[0]
    };

    /**
     * @public
     * @description 创建button <input type='button'> 对象，function (text,fn,caller,args)
     * @param {string|object}  text 文字信息
     * @param {string|function} fn 点击事件函数
     * @param {object}  caller 调用对象
     * @param {array}  args fn参数
     * @param {object} style 样式对象{text:'',backroud}
     * */
    this.button = function button(text, fn, caller, args, style, tip) {
        var me = Rsd || this;
        var btn = document.createElement('input');
        btn.type = 'button';
        btn.classList.add('x-text-button');
        btn.value = text;
        if (Rsd.isString(tip)) {
            btn.title = tip;
        }
        if (style && style.backgroundColor) {
            style.borderColor = style.borderColor || style.backgroundColor;
        }
        this.setElStyle(btn, style || {});
        if (me.isEmpty(fn)) {
            btn.title = "暂不可操作。";
            btn.classList.add("x-control-disabled");
        }
        else {
            btn.onclick = function (e) {
                me.callFunction(caller, fn, Rsd.arrayConcat(args || [],[e]));
            };
        }

        return btn;
    };

    /**
     * @description 样式简单 无背景的 <a>标签
     * @param {*} text 
     * @param {*} fn 
     * @param {*} caller 
     * @param {*} args 
     * @param {*} style 
     * @param {*} tip 
     */
    this.link = function link(text, fn, caller, args, style, tip) {
        var me = Rsd || this;
        var btn = document.createElement('a');
        btn.classList.add('x-text-link');
        btn.href = 'javascript:void(0);';
        btn.innerHTML = text;
        if (Rsd.isString(tip)) {
            btn.title = tip;
        }
        this.setElStyle(btn, style || {});
        if (me.isEmpty(fn)) {
            btn.title = "暂不可操作。";
            btn.classList.add("x-control-disabled");
        }
        else {
            btn.addEventListener('click',function (e) { 
                me.callFunction(caller, fn, Rsd.arrayConcat(args || [],[e]));
            },true);
        }

        return btn;
    };

    /**
     * @description 空白透明PNG 图片base64编码
     * */
    this.emptyImage = function emptyImage() {
        return '';
    };


    //以下方法 和dom操纵相关
    /**
     *
     * @desc 清空dom对象
     * */
    this.empty = function empty(ctrl) {

        if (this.isType(ctrl, Element)) {
            if (ctrl.childNodes && ctrl.childNodes.forEach) {
                ctrl.childNodes.forEach(function (node) {
                    ctrl.removeChild(node);
                });
                return;
            }
            ctrl.innerHTML = null;
        }

    };

    /**
     *@description 查询dom,
     * @param {dom} dom 默认为document,如果指定的dom在iframe 中，会导致查寻不到元素
     * @param {string} selector
     * */
    this.select = function select(dom, selector) {
        var _dom = dom;
        if (Rsd && Rsd.common && Rsd.common.ComponentX) {
            if (_dom instanceof Rsd.common.ComponentX) {
                _dom = _dom.dom;
            }
        }

        var _selector = selector;

        if (arguments.length == 1) {
            _selector = arguments[0];
            _dom = document;
        }
        //console.debug(_dom,_selector,_dom.querySelectorAll(_selector));
        //console.log(document.querySelectorAll(_selector));
        return Array.from(_dom.querySelectorAll(_selector));
    };

    /**
     * @description 为Dom绑定事件
     * @param {*} selector 
     * @param {*} eventName 
     * @param {*} fn 
     */
    this.bindEvent = function (selector, eventName, fn) {
        var list = this.select(selector);
        for (var i in list) {
            list[i][eventName] = fn;
        }
    };
    /**
     * @decription 获取各主机Controller 
     * @description 设置dom 元素style属性值
     * */
    this.setElStyle = function setElStyle(el, style, sizeUnit) {
        var _dom = el;
        var _style = style || {}; 
        if(this.isEmpty(_style))
        {
             return;
        }
        //console.log(_dom , _style , this.isType(_dom.style,CSSStyleDeclaration));
        //
        if (_dom && _dom.style && this.isType(_dom.style,CSSStyleDeclaration)) {

            var _sizeUnit = this.isEmpty(sizeUnit) ? 'px' : sizeUnit;
            //数字值属性处理
            var _list = [
                'width',
                'height',
                'maxWidth',
                'maxHeight',
                'minWidth',
                'minHeight',
                'lineHeight',
                'top',
                'right',
                'bottom',
                'left',
                'marginTop',
                'marginRight',
                'marginBottom',
                'marginLeft'
            ];
            var _p = null;

            for (var i in _list) {

                _p = _list[i];
                if (this.isNullOrUndefined(_style[_p])) {

                    continue;
                }

                if (this.isString(_style[_p])) {
                    if (_style[_p] == 'auto'
                        || _style[_p] == 'unset'
                        || _style[_p] == 'none'
                        || _style[_p] == 'ininitial'
                        || _style[_p] == 'revert') {
                        continue;
                    }
                    if (_style[_p].startWith('calc(') || _style[_p].endWith('%') || _style[_p].endWith('px') || _style[_p].endWith('rem') || _style[_p].endWith('em')) {

                        continue;
                    }

                    _style[_p] = parseFloat(_style[_p]) + _sizeUnit;

                    continue;
                }
                if (this.isNumber(_style[_p])) {

                    _style[_p] = _style[_p] + _sizeUnit;

                    continue;
                }

            }

            if (_dom.style.setProperty) {

                for (var p in _style) {

                    if (/^[0-9a-z]*$/.test(p)) {

                        _dom.style.setProperty(p, _style[p], 'important');

                    }
                    else {
                        //大写字母替换为小写，并在前插入 -
                        var _l = p.match(/[A-Z]/g);
                        var _new_p = p;
                        for (var i in _l) {
                            _new_p = _new_p.replaceByRegExp(_l[i], '-' + _l[i].toLowerCase());
                        }

                        _dom.style.setProperty(_new_p, _style[p], 'important');

                    }

                }
            } else {
                this.apply(_dom.style, _style);
            }
        }
    };

    /**
     * @description 标记dom 并 为dom 附加一个按钮
     * @param {*} targetDom dom 或 dom筛选表达式
     * @param {*} handler function(tpl);
     * @param {*} btnText button文字，如果未设置button文字,则不添加button按钮
     * @param {*} tip 文字提示 
     */
    this.patchButton = function patchButton(targetDom, handler, btnText, tip) {

        var _fn = function (dom) {
 
            //点击事件处理：拦截并屏蔽dom原点击事件
            dom.addEventListener ('click', function _on_click(e) {
                
                //console.log('阻止冒泡，阻止事件传播',e.target);
                //阻止冒泡，阻止事件传播
                e.stopPropagation();
                if (e && e.preventDefault) {
                    //阻止其他监听
                    e.preventDefault();
                }
                else {
                    window.event.returnValue = false;
                }
                 
                // 处理点击事件 
                if (handler) {

                    var target = e.target;
                 
                    while(target && (!target.getAttribute || target.getAttribute("dom-editable")!=='true'))
                    {
                        target = target.parentNode; //该节点不是 可以 编辑的目标dom对象 
                    }
                     
                    if(target)
                    {
                        handler(target);
                    } 
                    else
                    {
                        alert('不可以编辑元素');
                    }
                   
                }

                return false;
            },true);//true:设置为true以在捕获阶段触发,false:冒泡阶段,默认就是false
            
            //判断是否是 可编辑对象
            if(dom.getAttribute && dom.getAttribute("dom-editable")==='false')
            {
                 return;
            }
            //可编辑样式效果处理：加红色框
            //添加样式 
            Rsd.setElStyle(dom, {
                border: '1px red dotted',
                boxSizing: 'border-box'
            });

            //未设置button文字,不添加button按钮
            if (!btnText) {
                return;
            }

            //添加 button
            var id = dom.getAttribute('rsdbtn');
            var _btn = null;
            if (id) {
                _btn = dom.querySelector('#'+id);
            }
            else {
                id = 'btn_d_edit_' + Date.now();
                _btn = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
                _btn.setAttribute("id", id);
                _btn.setAttribute("dom-editable", false);
                _btn.style.color = "red";
                _btn.style.backgroundColor = "#a82323db";
                _btn.style.position = "absolute";
                _btn.style.width = "56px";
                _btn.style.height = "26px";
                _btn.style.zIndex = 9999;
                _btn.style.fontSize = "10px";
                _btn.style.cursor = "pointer";
                //_btn.style.transform = "translate(-7px,-4px)"; 
                _btn.setAttribute("viewBox", "-5 -5 50 2");
              
                dom.appendChild(_btn);
                dom.setAttribute('rsdbtn', id);
            }
  
            var _txt = document.createElementNS("http://www.w3.org/2000/svg", "text");
            _txt.textContent = btnText || '未设置';
            //x="100" y="100"
            _txt.setAttributeNS(null, "x","8");
            _txt.setAttributeNS(null, "y","0");
            _txt.setAttributeNS(null, "fill","#fff");
            //_txt.setAttributeNS(null, "width","55px");
            //_txt.setAttributeNS(null, "height","25px");
            while (_btn.lastChild) {
                _btn.removeChild(_btn.lastChild);
            }
            _btn.appendChild(_txt);

            if (tip) {
                var _title = document.createElementNS("http://www.w3.org/2000/svg", 'title');
                _title.textContent = tip;
                _btn.appendChild(_title);
            }

            _btn.onclick = function (e) {

                 //阻止冒泡
                e.stopPropagation();

                if (e && e.preventDefault) {
                    //阻止其他监听
                    e.preventDefault();
                }
                else {
                    window.event.returnValue = false;
                }

                if (handler) {
 
                    //从app.data中提取数据，app.data在页面数据加载时赋值
                    handler(dom);
                }
                else {
                    alert(' not set handler value ');
                }

                return false;
            };
        };

        //dom selector
        if (Rsd.isString(targetDom)) {
            var list = Rsd.select(targetDom);

            list.forEach(function (dom) {
                _fn(dom);
            });

            return list.length;
        }
        //js 的类型也是有作用域限制的，不同window下的即时相同类型名，也不是 同一个类型
        if (Rsd.isType(targetDom, NodeList)) {
            targetDom.forEach(function (dom) {
                _fn(dom);
            });
            return targetDom.length;
        }

        //dom 对象
        if (Rsd.isType(targetDom, Node)) {
            var dom = targetDom;
             
            _fn(dom);

            return 1;
        }

        console.error("不支持的dom对象", targetDom);
        return 0;
    }

    //以下方法和RPC相关

    /**
     * @description 加载本地配置文件，将配置项合并到service对象中
     * @param localConfig 本地服务配置文件 [{group:},{}]
     * @param callback function(data,state)
     */
    this.loadRouteTable = function loadRouteTable(localConfig, callback) {
        var me = this;

        //加载所有host 并 配置合并本地配置
        var _fnLoadAll = function (configList) {
            var _configMap = {};
            //console.log(configList);
            for (var i in configList) {
                _configMap[configList[i].group.toLowerCase()] = configList[i];
            }
            //console.log(_configMap); 

            var _hosts = Rsd.app.apiHosts;
            for (var i in _hosts) {
                me.loadServices(_hosts[i], _configMap, callback);
            }

        }

        //config 是配置文件
        if (this.isString(localConfig)) {
            //获取controller的本地配置
            var configStore = Rsd.create('Rsd.data.Store', {
                proxy: {
                    url: localConfig,
                    method: 'get'
                }
            });

            configStore.load({}, function (groupList) {
                _fnLoadAll(groupList);
            });

            return;
        }
        //config 是配置数组
        if (this.isArray(localConfig) || this.isEmpty(localConfig)) {
            _fnLoadAll(localConfig || []);
            return;
        }

    },

    /**
           { 
               text:'电商服务',
               name:'',
               url:'http://hermes.redmicro.cn/', 
               index:'dev/index', 
               isLoaded:false//已加载
               controllers:[{name:'',path:'',GetDescription:''},{},{}],
               routeList:'/dev/getroutetable',//服务端路由表接口
               useSSL:false
            },
            
    * @decription 将一个apihost加载到本地 
    * @param host  一个独立API项目 {name:'',text:'',url:'',useSSL:true,controllers:[]}
    * @param config 配置文件
    * @param callback function(data,state)
    */
    this.loadServices = function loadServices(host, config, callback) {
            //console.log(host);
            //console.log(config);  

            var _config = config || {};
            //获取主机的controlls 的api
            var _fnLoad = function (host, data) {
                //console.log(data);
                //controller list
                var _controllers = [];
                for (var i in data.data) {
                    //合并本地配置 => controller 级别 配置项
                    var group = (data.data[i].Name || data.data[i].name).toLowerCase();
                    //API接口发现
                    var _url = host.url + (data.data[i].Path || data.data[i].path).toLowerCase().trim('/') + "/" + ((data.data[i].GetDescription || data.data[i].GetDescription || 'GetDes').trim('/'));

                    if (!Rsd.isEmpty(host.name)) {
                        group = host.name + "." + group;
                    }

                    var gConfig = _config[group] || {};
                    //console.log(gConfig);
                    //初始化contoller
                    var _ctrl = {
                        "group": group,
                        "url": _url,
                        "useSSL": host.useSSL,
                        "errorHandler": gConfig.errorHandler || "errorHandler",
                        "failureHandler": gConfig.failureHandler || "failureHandler",
                        "successHandler": gConfig.successHandler || "emptyHandler",//默认按非交互操作处理
                        "api": {}
                    };

                    _controllers.push(_ctrl);

                    Rsd.services[_ctrl.group] = _ctrl;
                }

                var _data = { success: true, msg: '', data: { total: 0, pagecount: 1, pageindex: 0, pagesize: 1000, modeltype: '', rows: [] } };

                var _fnConfig = function (__data) {

                    for (var i in __data.data) {
                        var key = this.group + '.' + __data.data[i].MethodName.toLowerCase();

                        var apiService = Rsd.app.getService(key);
                        apiService.parent = Rsd.app;
                        apiService.data = __data.data[i];
                        _data.data.total++;
                        _data.data.rows.push(apiService);
                    }

                    var gConfig = _config[this.group] || {};
                    //合并本地配置 => api级别配置项
                    if (!Rsd.isEmpty(gConfig)) {
                        //console.log(gConfig);
                        //api 指定配置
                        for (var i in gConfig.api) {
                            var _c = gConfig.api[i];//特定 api的配置项
                            var key = this.group + '.' + gConfig.api[i].name.toLowerCase();//console.log(key);
                            var apiService = Rsd.app.getService(key);
                            if (apiService) {
                                apiService.errorHandler = _c.errorHandler || apiService.errorHandler;
                                apiService.failureHandler = _c.failureHandler || apiService.failureHandler;
                                apiService.successHandler = _c.successHandler || apiService.successHandler;
                                apiService.progressHandler = _c.progressHandler || apiService.progressHandler;
                                apiService.ajaxType = _c.ajaxType || apiService.ajaxType;
                                apiService.local = { method: 'get', url: _c.localUrl };
                            }
                        }
                    }

                    Rsd.callFunction(Rsd.app, callback, [__data, 'serviceLoaded']);

                }

                for (var i in _controllers) {
                    Rsd.loadServiceApi(_controllers[i], _fnConfig);
                }

                Rsd.callFunction(Rsd.app, callback, [_data, 'loaded']);
            };

            if (host.isLoaded) {
                return;
            }
            if (Rsd.isEmpty(host.url)) {
                console.error("apihost未设置url属性。")
                return;
            }

            Rsd.services = Rsd.services || {};
            //加载策略1：根据路由表返回加载
            if (!Rsd.isEmpty(host.routeList)) {
                var _services = Rsd.create('Rsd.data.Store', {
                    host: host,
                    proxy: {
                        url: host.url + host.routeList,
                        method: 'get'
                    }
                });

                _services.load({}, function (data) {

                    Rsd.callFunction(Rsd.app, _fnLoad, [this.host, data]);

                });
                return;
            }

            //加载策略2：指定要加的controller
            if (!Rsd.isEmpty(host.controllers)) {
                //适配返回数据
                Rsd.callFunction(Rsd.app, _fnLoad, [host, { data: host.controllers }]);
                return;
            }

            //提示错误：未设置要加载的数据
            if (Rsd.isEmpty(host.routeList) && Rsd.isEmpty(host.controllers)) {
                console.error("apihost未设置routeList属性 ，且未设置controllers属性。")
                return;
            }
    };

    /**
    * @description  加载单个controller,获取controller拥有的API（方法）
    * @param {*} controller  {group:'',url:'',useSSL:false,errorHandler:'',failureHandler:'',successHandler:'',api:{}} 获取api描述地址URL
    * @param {*} callback 
   */
    this.loadServiceApi = function loadServiceApi(controller, callback) {

        controller.isLoading = true;
        //console.log(controller);
        var _group = controller.group.toLowerCase();
        var _ssl = controller.useSSL;

        //console.log('load =>' + _group);

        Rsd.httpRequest({ url: controller.url, method: 'GET' }, {}, function (data) {

            if (data.success === false) {
                console.error('获取API描述（' + controller.url + ')失败:', data);
                return;
            }
            var list = data.data || data || [];

            for (var j in list) {
                var item = list[j];

                if (!Rsd.isString(item.Url)) {
                    console.error('属性Url的值（' + item.Url + '）无效', item);
                    item.Url = '';
                }
                if (_ssl && item.Url.startWith('http://')) {
                    item.Url = 'https://' + item.Url.substr(7);
                }

                var config = {
                    key: _group + '.' + item.MethodName.toLowerCase(),
                    name: item.MethodName.toLowerCase(),
                    group: _group,
                    text: item.Description,
                    parent: Rsd.app,
                    errorHandler: controller.errorHandler || '',
                    failureHandler: controller.failureHandler || '',
                    successHandler: controller.successHandler || '',
                    progressHandler: controller.progressHandler || '',
                    ajaxType: item.ajaxType || 'ajax',
                    local: { method: 'get', url: '' },
                    server: {
                        url: item.Url || '',
                        method: item.Method || 'POST',
                        contentType: item.ContentType || 'application/x-www-form-urlencoded',
                        //dataType: 'json',
                        async: true
                    }
                }
                if (Rsd.services[_group]) {
                    Rsd.services[_group].api[item.MethodName.toLowerCase()] = new Rsd.data.Service(config);
                }
                else {
                    console.error(_group + '不存在');
                }

            }
            controller.isLoading = false;

            //console.log('loaded =>' + _group);
            if (Rsd.isFunction(callback)) {
                callback.call(controller, data);
            }

        }, '加载服务');
    };

    /**
     *
     * @description 请求API服务,接口数据加载中时，自动等待。
     * @param name
     * @param data
     * @param callback 请求成功（status==200）回调方法,且优先执行，执行返回true时,后续方法（success,failure）继续执行，返回false时，后续方法终止
     * @param msg
     * @param timeout 毫秒
     * */
    this.requestService = function requestService(name, data, callback, msg, timeout) {

        if (Rsd.isEmpty(name)) {
            throw new Error('param [name] is null when call  Rsd.requestService method.');
        }
        var _name = name.toLowerCase();
        var _group = _name.substr(0, _name.lastIndexOf('.'));
        var _method = _name.substr(_name.lastIndexOf('.') + 1);

        //组
        var serviceGroup = Rsd.services[_group];

        if (Rsd.isEmpty(serviceGroup)) {
            var _error = '服务[' + name + ']不存在,请先注册';
            alert(_error);
            console.error(_error);
            return;
        }

        var _fn = function () {

            var apiService = serviceGroup.api[_method];
            if (Rsd.isEmpty(apiService)) {
                var _error = '服务[' + name + ']不存在,请确认';
                alert(_method);
                console.error(_error);
                return;
            }

            apiService.request(data, callback, msg||"正在处理，请稍后...", timeout);

        }

        var _timer = 0;
        var _request = function () {

            if (serviceGroup.isLoading) {
                //如果正在加载服务 延时200毫秒 最多延时5次
                _timer++;
                if (_timer > 10) {
                    Rsd.warn('系统繁忙，请稍后再试');
                    return;
                }
                //console.log(_timer + '- call ' + _name + ' => controller ' + serviceGroup.group + ' is loading.');
                setTimeout(_request, 200 * _timer);
            }
            else {
                _fn();
            }

        }
        if (Rsd.isEmpty(serviceGroup.api)) {

            if (serviceGroup.isLoading) {
                _request();
            }
            else {
                Rsd.loadServiceApi(serviceGroup, _fn);
            }

        } else {
            _fn();
        }

    };

    /**
     * @description 请求API服务,接口数据加载中时，自动等待。返回Promise对象
     * @param {*} serviceName 
     * @param {*} args 
     * @returns 
     */
    this.requestServicePromise = function requestServicePromise(serviceName, args, msg) {

        var _p = new Promise(function (resolve, reject) {

            //console.log('this is p',_p);
            Rsd.requestService(serviceName, args || {}, function (resp) {

                if (resp.success) {
                    if (resolve) {
                        resolve(resp);
                    }
                    //console.log('success return ');
                    return;
                }
                //console.log('this promise status is (before reject ) ',_p.status);
                if (reject) {
                    reject(resp);
                }

                //console.log('this promise status is (after reject ) ',_p.status);
                return false;

            }, msg);
        });

        return _p;
    };

    //以下方法 和文件处理相关
    this.encodeBase64 = function encodeBase64(str) {
        return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
            return String.fromCharCode('0x' + p1);
        }));
    }
    /**
     * @description 解码base64
     * @param {*} base64 
     * @returns 
     */
    this.decodeBase64 = function decodeBase64(base64) {
        var len = base64.length;
        var buffer = new Uint8Array(len / 4 * 3 | 0);
        var i = 0;
        var outptr = 0;
        var last = [0, 0];
        var state = 0;
        var save = 0;
        var rank;
        var code;
        var undef;
        var base64_ranks = new Uint8Array([
            62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
            , -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
            , 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
            , -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
            , 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
        ]);

        while (len--) {
            code = base64.charCodeAt(i++);
            rank = base64_ranks[code - 43];
            if (rank !== 255 && rank !== undef) {
                last[1] = last[0];
                last[0] = code;
                save = (save << 6) | rank;
                state++;
                if (state === 4) {
                    buffer[outptr++] = save >>> 16;
                    if (last[1] !== 61 /* padding character */) {
                        buffer[outptr++] = save >>> 8;
                    }
                    if (last[0] !== 61 /* padding character */) {
                        buffer[outptr++] = save;
                    }
                    state = 0;
                }
            }
        }
        // 2/3 chance there's going to be some null bytes at the end, but that
        // doesn't really matter with most image formats.
        // If it somehow matters for you, truncate the buffer up outptr.
        return buffer;
    }
    /**
     * 
     * @param {*} base64 
     * @returns 
     */
    this.decodeBase64ToString = function decodeBase64ToString(base64) {
        var utf8Decoder = new TextDecoder('utf-8');
        return utf8Decoder.decode(Rsd.decodeBase64(base64));
    }
    /**
     * 
     * @param {*} str 
     */
    this.stringToBlob = function stringToBlob(str, type) {
        return new Blob([str], { type: type || "text/plain;charset=utf-8" });
    }
    /**
     * 
     * @param {*} str 
     * @param {*} type 
     * @param {*} name 
     * @returns 
     */
    this.stringToFile = function stringToFile(str, type, name) {
        var blob = this.stringToBlob(str, (type || "text/plain") + ";charset=utf-8");
        return new File([blob], name, { type: type || "text/plain" });
    }
    /**
     * @description  canvas对象转为Blob对象
     * @param {*} canvas 
     * @param {*} type 
     * @returns 
     */
    this.canvasToBlob = function canvasToBlob(canvas, type/*, ...args*/) {
        type = type || "image/png";
        if (canvas.mozGetAsFile) {

            return canvas.mozGetAsFile("canvas", type);
        }
        if (canvas.msToBlob && /^\s*image\/png\s*(?:$|;)/i.test(type)) {

            return canvas.msToBlob();
        }

        var args = Array.prototype.slice.call(arguments, 1);
        //
        var dataURI = canvas.toDataURL.apply(canvas, args);
        var header_end = dataURI.indexOf(",");
        var data = dataURI.substring(header_end + 1);
        var is_base64 = /\s*;\s*base64\s*(?:;|$)/i.test(dataURI.substring(0, header_end))
        var blob = null;

        if (Blob.fake) {
            // no reason to decode a data: URI that's just going to become a data URI again
            blob = new Blob
            if (is_base64) {
                blob.encoding = "base64";
            } else {
                blob.encoding = "URI";
            }
            blob.data = data;
            blob.size = data.length;

            return blob;
        }

        if (Uint8Array) {

            if (is_base64) {
                blob = new Blob([this.decodeBase64(data)], { type: type });
            } else {
                blob = new Blob([decodeURIComponent(data)], { type: type });
            }

            return blob;
        }

        return blob;
    };

    /**
    * @description canvas对象转化为DataURL对象,跨域时，会被安全拦截阻止
    * @param {*} canvas 
    * @param {*} format 文件格式
    * @param {*} quality 
    * @returns 
    */
    this.canvasToDataURL = function canvasToDataURL(canvas, format, quality) {
        return canvas.toDataURL(format || 'image/jpeg', quality || 1.0);
    };
    /**
     * @description DataURL对象转化为canvas对象
     * @param {*} dataurl 
     * @param {*} callback 
     */
    this.dataURLToCanvas = function dataURLToCanvas(dataurl, callback) {
        var canvas = document.createElement('CANVAS');
        var ctx = canvas.getContext('2d');
        var img = new Image();
        img.onload = function () {
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            callback(canvas);
        };
        img.src = dataurl;
    };
    /**
     * @description image对象转化为canvas对象
     * @param {*} img 图片地址 或 dom对象；当图片地址跨域时，需要传递image dom 对象
     * @param {*} callback 
     */
    this.imageToCanvas = function imageToCanvas(img, callback) {

        var canvas = document.createElement('CANVAS');
        var ctx = canvas.getContext('2d');

        if (img instanceof HTMLElement) {
            img.crossOrigin = "";
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            callback(canvas);

            return;
        }

        var src = img;
        var _img = new Image();
        _img.crossOrigin = "anonymous";
        _img.src = src;
        _img.onload = function () {
            canvas.width = _img.width;
            canvas.height = _img.height;
            ctx.drawImage(_img, 0, 0);
            callback(canvas);
        };
    };

    /**
     * @description anvas对象转化为image对象
     * @param {*} canvas 
     * @returns 
     */
    this.canvasToImage = function canvasToImage(canvas) {
        var img = new Image();
        img.src = canvas.toDataURL('image/jpeg', 1.0);
        return img;
    };

    /**
    * @description File/Blob对象转DataURL对象
    * @param {*} obj 
    * @param {*} callback
    */
    this.blobToDataURL = function blobToDataURL(obj, callback) {
        var a = new FileReader();
        a.readAsDataURL(obj);
        a.onload = function (e) {
            callback(e.target.result);
        };
    };

    /**
     * @description DataURL转Blob对象
     * @param {*} dataurl 
     * @returns 
     */
    this.dataURLToBlob = function dataURLToBlob(dataurl) {
        var arr = dataurl.split(',');
        var mime = arr[0].match(/:(.*?);/)[1];
        var bstr = atob(arr[1]);
        var n = bstr.length;
        var u8arr = new Uint8Array(n);
        while (n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], { type: mime });
    };

    /**
     * @description base64转Blob对象
     * @param {*} base64 
     * @returns 
     */
    this.base64ToBlob = function base64ToBlob(base64) {

        var parts = base64.split(";base64,");
        var contentType = parts[0].split(":")[1];
        var raw = window.atob(parts[1]);
        var rawLength = raw.length;
        var uint8Array = new Uint8Array(rawLength);
        for (var i = 0; i < rawLength; i++) {
            uint8Array[i] = raw.charCodeAt(i);
        }
        return new Blob([uint8Array], { type: contentType });
    };

    /**
     * @description 下载文件
     * @param {*} base64  文件内容
     * @param {*} fileName  文件名
     * @returns 
     */
    this.downloadFromBase64 = function downloadFromBase64(base64, fileName) {

        if (typeof URL==='undefined' || !base64) {
            return;
        }
        var aLink = document.createElement("a"); // 创建一个a标签
        var blob = this.base64ToBlob(base64);
        var event = document.createEvent("HTMLEvents");
        event.initEvent("click", true, true);
        aLink.download = fileName + blob.type.split("/")[1]; // 使用时间戳给文件命名
        aLink.href = URL.createObjectURL(blob);
        aLink.click();
    };

    /**
     * @description 下载文件
     * @param {*} blob 文件内容
     * @param {*} fileName  文件名
     * @returns 
     */
    this.downloadFromBlob = function downloadFromBlob(blob, fileName) {
        if (typeof URL==='undefined'||blob===null) {
            return;
        }
        var a = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
        var name = (fileName || blob.name || 'download') + blob.type.split("/")[1]; // 

        a.download = name
        a.rel = 'noopener' // tabnabbing
        // Support blobs
        a.href = URL.createObjectURL(blob);
        setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4); // 40s
        setTimeout(function () {
            try {
                a.dispatchEvent(new MouseEvent('click'))
            } catch (e) {
                var evt = document.createEvent('MouseEvents')
                evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
                    20, false, false, false, false, 0, null)
                a.dispatchEvent(evt)
            }
        }, 0);
    };
    /**
     * 
     * @param {*} img 图片url或图片dom对象；当图片地址跨域时，需要传递image dom 对象
     * @param {*} fileName 
     */
    this.downloadImage = function downloadImage(img, fileName) {

        if(URL === undefined)
        {
            return;            
        }
        //fetch 不是所有浏览器都支持
        if (window.fetch) {
            var img_url = "";
            if (this.isString(img)) {
                img_url = img;
            }
            if (img.src) {
                img_url = img.src;
            }

            window.fetch(img_url)
                .then(function (res) {
                    res.blob()
                        .then(function (blob) {

                            var a = document.createElement('a');
                            var url = window.URL.createObjectURL(blob);

                            a.href = url;
                            a.download = fileName;
                            a.click();
                            window.URL.revokeObjectURL(url);
                        });
                });
            return;
        }
        //图像处理不完整
        Rsd.imageToCanvas(img, function (canvas) {

            var blob = Rsd.canvasToBlob(canvas);

            if (blob == null) {
                console.error('canvas To Blob failed');
                return;
            }
            Rsd.downloadFromBlob(blob, fileName);
        });

    }
    /**
     * 
     * @param {*} len 
     * @param {*} radix 
     * @returns 
     */
    this.getUUID = function getUUID(len, radix) {
        //获取UUID
        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(
            ""
        );
        var uuid = [], i;

        len = len || 32;
        radix = radix || chars.length;

        if (len) {
            // Compact form
            for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
        } else {
            // rfc4122, version 4 form
            var r;

            // rfc4122 requires these characters
            uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
            uuid[14] = "4";

            // Fill in random data.  At i==19 set the high bits of clock sequence as
            // per rfc4122, sec. 4.1.5
            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | (Math.random() * 16);
                    uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
                }
            }
        }
        return uuid.join("");
    }
    //console.debug(Date.now(),'redjs end');
}

